Trait object in different threads -> value moved into closure here and borrowed after move

I am trying to implement a trait like this:

// This is the trait
pub trait ProtocolTrait {
    fn send(&mut self, ad: &[u8], msg: Vec<u8>) -> (Vec<u8>);
    fn receive(&mut self, ad :&[u8], ct: Vec<u8>) -> (Vec<u8>);

//then have some struct and its impl
pub fn get_protocol_my_type(protocol_type: ProtocolType) -> Box<dyn ProtocolTrait + Send>{
... // this method can return a trait object

Then I want to use this trait object in a concurrent way. It looks like this:

    let mut protocol = Protocol::get_protocol_my_type(ProtocolType::protocol1);
    let ad = generate();
    let (_sender, _receiver)  = mpsc::channel();

//in spawn thread, we can call the trait object function, get the ct and send it to the channel. 
    thread::spawn(move || {
        for i in 1..10 {
            let dt = Local::now();
            println!("alice sent at {}", dt.format("%Y-%m-%d %H:%M:%S").to_string());
            let ct = protocol.send(&ad, vec![i]);

        for receivedCipher in _receiver {
             let pt = protocol.receive(&ad, receivedCipher);
            let dt = Local::now();
            println!("Bob received: {:?} at {}", pt, dt.format("%Y-%m-%d %H:%M:%S").to_string());

and I got this error:

229 |     thread::spawn(move || {
    |                   ------- value moved into closure here
233 |             let ct = protocol.send(&ad, vec![i]);
    |                      -------- variable moved due to use in closure
241 |              let pt = protocol.receive(&ad, receivedCipher);
    |                       ^^^^^^^^ value borrowed here after move

You can assume that I call the protocol.send to encrypt the message and send, and in main thread receive the ciphertext and call protocol.receive to decrypt.

I know the error is because the trait object has moved to closure and then been removed.
So I was wondering if there is any way so that I can use this trait object in both threads??

Thanks in advance!

If a method takes &mut self, that indicates that it requires exclusive access, so it cannot be shared in multiple places, at least not directly. That's true even regardless of threading.

But it's also a move closure, which makes sense because std::thread::spawn requires 'static closures, can't borrow from local data. So you need shared ownership and shared mutability, which is where you may want to wrap it as Arc<Mutex<T>>.

1 Like

So I need to first consider changing &mut self to &self and then wrap the trait object to Arc<Mutex>, do I understand correctly?
Thank you!

You only need the Mutex if you need to share mutably. If your methods can work with just &self then you don't need that, only the Arc for shared ownership.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.