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

#1
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?

#2

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.

#3

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
#4

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.

#5

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