Tokio future is not send error

I have an app which calculates the states of multiple games and have a countdown after a game finishes.
What I am trying to do is to spawn a future with tokio::spawn to run the countdown elsewhere not to block the other games. The problem is a compile time error telling me the future is not send which I am trying to understand.

//...
        tokio::spawn(countdown(tx.clone(), store.clone(), game, game_id));
//...

async fn countdown(
    tx: Arc<Mutex<Sender<(String, String)>>>,
    store: Store,
    last_game: Game,
    game_id: i32,
) {
    for i in 0..90 {
        tokio::time::delay_for(std::time::Duration::from_secs(1)).await;
        let _ = tx
            .lock()
            .map(|tx| tx.send(("countdown".into(), (90 - i).to_string())));
    }
    let game = Game {
        id: last_game.id + 1,
        ..Default::default()
    };
    let _ = store.set_active_game_state_owned(game_id, game).await;
}
50  |         tokio::spawn(countdown(tx.clone(), store.clone(), game, game_id));
    |         ^^^^^^^^^^^^ future returned by `countdown` is not `Send`
    |
   ::: /home/nikos/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/task/spawn.rs:127:21
    |
127 |         T: Future + Send + 'static,
    |                     ---- required by this bound in `tokio::task::spawn::spawn`
    |
    = help: the trait `std::marker::Send` is not implemented for `dyn std::future::Future<Output = std::result::Result<(), state::err::Err>>`
note: future is not `Send` as it awaits another future which is not `Send`
   --> game/src/game.rs:97:13
    |
97  |     let _ = store.set_active_game_state_owned(game_id, game).await;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here on type `std::pin::Pin<std::boxed::Box<dyn std::future::Future<Output = std::result::Result<(), state::err::Err>>>>`, which is not `Send`

What is the error message?

oh sorry I am attaching that too

I have included the cargo check output @alice

You should include + Send in your trait objects. For convenience I recommend using the BoxFuture alias.

type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a + Send>>;

and returning -> BoxFuture<'static, Result<(), Err>>, or perhaps with the lifetime '_ depending on whether it borrows from store.

logically what prevents anything from being Send I dont quite get that concept. I mean send is just a marker trait so there is really no real code to enforce that so why not unless !Send is specified everything is not Send by default ?

Given a trait MyTrait, there is a special type called a trait object written as dyn MyTrait. All concrete types (structs/enums/etc.) that implement MyTrait can be turned into a dyn MyTrait, which means that it must be able to store any such value, and therefore the compiler forgets any additional information about the value when you use the trait object type.

For example in our case, it is possible to convert any future into a dyn Future, and this includes futures that do not implement Send. Since dyn Future can store values that are not Send, the trait object cannot be Send either.

Is there any way to express async fn without using dyn Future as return type ?

Yes. If you show me the definition of set_active_game_state_owned, I can show you how to change it.

    fn set_active_game_state(&self, game_id: i32, game: &'a Game) -> Future<'a, ()> {
        let client = self.redis_client.clone();
        Box::pin(async move {
            client
                .run(|mut con| async move {
                    let state = serde_json::to_string(game).map_err(Into::<Err>::into)?;
                    con.set::<_, _, ()>(RedisNameSpaces::GameState(game_id), state)
                        .await
                        .map(|()| (con, ()))
                })
                .await
                .map_err(Into::into)
        })
    }

owned is basically the same thing the only difference that one owns the game while this gets a reference to it

In this case, you can use the impl Trait syntax.

fn set_active_game_state(&self, game_id: i32, game: &'a Game) -> impl Future<Output = Result<(), Err>> + 'a {
    let client = self.redis_client.clone();
    async move {
        client
            .run(|mut con| async move {
                let state = serde_json::to_string(game).map_err(Into::<Err>::into)?;
                con.set::<_, _, ()>(RedisNameSpaces::GameState(game_id), state)
                    .await
                    .map(|()| (con, ()))
            })
            .await
            .map_err(Into::into)
    }
}

Unlike dyn Trait, the impl Trait syntax is not a specific type, rather it is a way to tell the compiler "please figure out what the actual type here should be and write it for me when compiling the program".

I actually tried that but this function belongs to a trait called AsyncStore and seems impl trait cannot be used in that context

It is generally not recommended to try to put async functions on traits. If you really want to do it, you have to use trait objects.

I see thank you

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.