Passing references of struct instances that do not implement Send to async fn

pub trait Captures<'a> {}

 impl<'a, T> Captures<'a> for T {}    
 
fn handle_message<'a: 'c,'b: 'c,'c> (
    message: &'a Message, 
    //send_msg: &'b Fn(Message) -> Result<(), SendError<Message>>
    sink: &'b mut Sink<SinkItem = Message, SinkError = Error>
) -> impl Future<Output = ()> + Captures<'a> + Captures<'b> + 'c {
    async move {
        let result = if let Ok(message_body) = message.to_text() {
            sink.start_send(Message::from(message_body));
        };
    }
}

async fn handle_stream(ws_stream: WebSocketStream<TcpStream>) {
    let (mut sink, mut stream) = ws_stream.split();
    tokio::spawn_async(
        async move {
            let _ = sink.start_send(Message::from("Hello World!"));
            while let Some(message) = await!(stream.next()) {
                let message = message.unwrap();
                await!(handle_message(&message, &mut sink));
             }
         }
      )
 }

outputs this error

error[E0277]: `dyn futures::sink::Sink<SinkError=tungstenite::error::Error, SinkItem=tungstenite::protocol::message::Message>` cannot be sent between threads safely
  --> server\src\game_server\mod.rs:62:22
   |
20 |         let thread = tokio::spawn_async(
   |                      ^^^^^^^^^^^^^^^^^^ `dyn futures::sink::Sink<SinkError=tungstenite::error::Error, SinkItem=tungstenite::protocol::message::Message>` cannot be sent between threads safely
   |

Interestingly, if the call to handle message is substituted with

    async {
        let result = if let Ok(message_body) = message.to_text() {
            sink.start_send(Message::from(message_body));
        };
    }

the program compiles.

Any idea why putting that code into its own method triggers an error, and what I can do to rectify it?

You could try adding + Send to the sink argument in handle_message, this is not done automatically for you (unlike the return type) so the compiler doesn’t know that the sink you are passing in is Send.

That worked. Thank you so much for all the advice you've posted on Rust, as you may have noticed I've already been making use of your posts :slight_smile:

Also, follow up question:

Why does it need to implement the Send trait? As far as I can tell this is all happening on one thread.

2 Likes

I'm not too well versed in async operations but if I recall correctly, they may sometimes run on a seperate thread (Therefore requiring Send) or sometimes not require another thread.

Yeah, specifically tokio::spawn_async pushes the future off onto a threadpool that may move it between threads to try and balance the load. There are single-threaded executors if you can’t have a Send future, but most of the ecosystem is targeting this sort of threadpool based executor.

1 Like