Kevin
Kevin

Reputation: 3348

How can I return a tuple to javascript from wasm where elements have non Copy/Clone traits?

I have a problem where I need to return a tuple from a wasm function as the following:

#[wasm_bindgen]
pub fn channel() -> (futures::channel::mpsc::Sender<i32>, futures::channel::mpsc::Receiver<i32>) {
    let (mut tx, mut rx) = futures::channel::mpsc::channel(32);
    return (tx, rx)
}

(I need to return the sender and receiver with separate ownership)

It is not possible to return a tuple as explained here: https://stackoverflow.com/a/75064902/14923227

However, the provided workaround does not work for me since Receiver does not implement Copy or Clone trait.

I have tried solving this by returning a Struct Pair instead with getter methods for sender and receiver:

#[wasm_bindgen]
struct WrappedReceiver {
    receiver: futures::channel::mpsc::Receiver<i32>
}

#[wasm_bindgen]
pub struct Pair {
    sender: futures::channel::mpsc::Sender<i32>,
    receiver: WrappedReceiver
}

#[wasm_bindgen]
impl Pair {
    #[wasm_bindgen(getter)]
    pub fn receiver(&self) -> WrappedReceiver {
        return self.receiver
    }
}

This compiles with the same issue, that WrappedReceiver does not implement Copy trait.

I can however wrap the Receiver in Rc with the following:


#[wasm_bindgen]
#[derive(Clone)]
pub struct WrappedSender(futures::channel::mpsc::Sender<i32>);

#[wasm_bindgen]
#[derive(Clone)]
pub struct WrappedReceiver(Rc<futures::channel::mpsc::Receiver<i32>>);

#[wasm_bindgen]
pub struct Pair {
    #[wasm_bindgen(getter_with_clone)]
    pub sender: WrappedSender,
    #[wasm_bindgen(getter_with_clone)]
    pub receiver: WrappedReceiver
}

#[wasm_bindgen]
impl Pair {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Pair {
        let (mut tx, mut rx) = futures::channel::mpsc::channel(32);
        Pair {
            sender: WrappedSender(tx),
            receiver: WrappedReceiver(Rc::new(rx))
       }
    }
}

It not works to destruct the Pair in JavaScript with the following:

import * as wasm from './pkg'
const { receiver, sender } = new wasm.Pair()

I have some other place where I need to use the futures::channel::Receiver<i32> with exclusive ownership &mut (ideally I want it to be a futures stream).

#[wasm_bindgen]
pub fn use_receiever(receiver: &mut WrappedReceiver) {
    let rx: Pin<Box<dyn futures::stream::Stream<Item = i32>>> = receiver.0.boxed_local();
}

This is because I cannot convert WrappedReceiver to a &mut futures::channel::mpsc::Receiver<i32> I think.

playground link

I could use a Rc<RefCell<futures::channel::mpsc::Receiver<i32>>> which would allow me to borrow mutually:

#[wasm_bindgen]
pub fn use_receiever(receiver: &mut WrappedReceiver) {
    let rx: &mut futures::channel::mpsc::Receiver<i32> = &mut receiver.0.borrow_mut();
}

playground link

This seems to work, but it feels like I am adding to many constructions. I also only want single ownership of futures::channel::mpsc::Receiver<i32>, whereas RefCell is more suitable for multiple ownership?

Ideally I would like WrappedReceiver to be the following:

#[wasm_bindgen]
struct WrappedReceiver {
    receiver: Pin<Box<dyn futures::stream::Stream<Item = i32>>>,
}

Is there a way to achieve this?

Upvotes: 0

Views: 83

Answers (1)

Kevin
Kevin

Reputation: 3348

It is possible to use Option which has the take method which moves the value out from the option. This makes it possible to temporarily take ownership of Receiver.

#[wasm_bindgen]
pub struct Pair {
    sender: futures::channel::mpsc::Sender<i32>,
    receiver: Option<futures::channel::mpsc::Receiver<i32>>
}

#[wasm_bindgen]
impl Pair {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Pair {
        let (tx, rx) = futures::channel::mpsc::channel(32);
        Pair {
            sender: tx,
            receiver: Some(rx)
       }
    }

    #[wasm_bindgen(getter)]
    pub fn receiver(&mut self) -> WrappedReceiver {
        WrappedReceiver { receiver: self.receiver.take().unwrap().boxed_local() }
    }

    #[wasm_bindgen(getter)]
    pub fn sender(&self) -> WrappedSender {
        WrappedSender { sender: self.sender.clone() }
    }
}

It is then possible to de-structure the object in JS with the following:

const { receiver, sender } = new wasm.Pair()

Upvotes: 0

Related Questions