Iterator not Send, but the data it yields is

More async headache!

I have an iterator which holds some !Send !Sync data (yes, still pest::Pairs!) but I don't want to pass it across an await, only the data it yields (a beautiful plain String):

match roller.dices() {
    Ok(dices) => match dices.next() {
        Some(dice) => {
            let dice = dice.clone();
            match process_roll_str(&dice, ctx, msg).await {
                Ok((name, res)) => format!("{} reroll `{}`: {}", name, input, res),
                Err(e) => e.to_string(),
            }
        }
        _ => "No dice to reroll".to_string(),
    },
    Err(e) => e.to_string(),
}

You can see I tried to clone my String to avoid passing anything from the iterator in the await but still:

note: future is not `Send` as this value is used across an await
   --> src\main.rs:300:31
    |
297 |                 Ok(dices) => match dices.next() {
    |                    ----- has type `caith::Dices<'_>` which is not `Send`
...
300 |                         match process_roll_str(&dice, ctx, msg).await {
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `dices` maybe used later
...
306 |                 },
    |                  - `dices` is later dropped here
    = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)

Thanks for any insight and teaching :slight_smile:

The problem seems to be caused by the Dices destructor running after the await. Try forcing it to run early by calling drop explicitly:

match roller.dices() {
    Ok(dices) => match dices.next() {
        Some(dice) => {
            drop(dices); // drop the iterator early
            let dice = dice.clone();
            // ...

Thanks for your answer :slight_smile: but it doesn't help, unfortunately :slightly_frowning_face:

Hmm. You might need to drop it even earlier, so the compiler sees that it is guaranteed not to be used after the await:

match roller.dices() {
    Ok(dices) => {
        let next = dices.next();
        drop(dices);
        match next {
            Some(dice) => {
                let dice = dice.clone();
                // ...

Thanks again, I don't know if this work but I reorganized the code like this to avoid the issue:

let dice = match roller {
            Some(roller) => match roller.dices() {
                Ok(dices) => match dices.next() {
                    Some(dice) => Ok(dice),
                    _ => Err("No dice to reroll".to_string()),
                },
                Err(e) => Err(e.to_string()),
            },
            None => Err("No previous roll".to_string()),
        };
        match dice {
            Ok(dice) => match process_roll_str(&dice, ctx, msg).await {
                Ok((name, res)) => format!("{} reroll `{}`: {}", name, input, res),
                Err(e) => e.to_string(),
            },
            Err(err) => err,
        }
1 Like

Unfortunately the Send check for futures is based on scope information only, so drop doesn't work for doing it.

2 Likes

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.