Future + Send and holding a MutexGuard for a short time (not across a yield)

Continuing the discussion from Manually drop MutexGuard still raise `Future is not Send' error:

I just ran into the same problem, but I think I found a workaround that might help in some cases:

use std::future::Future;
use std::sync::Mutex;

async fn nop() {}

async fn foo() {
    let mutex = Mutex::new(());
    // I need to comment-in these curly braces for the `Future` to be `Send`
    //{
        let guard = mutex.lock().unwrap();
        drop(guard);
    //}
    nop().await;
}

fn main() {
    let _: Box<dyn Future<Output = ()> + Send> = Box::new(foo());
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error: future cannot be sent between threads safely
  --> src/main.rs:17:50
   |
17 |     let _: Box<dyn Future<Output = ()> + Send> = Box::new(foo());
   |                                                  ^^^^^^^^^^^^^^^ future returned by `foo` is not `Send`
   |
   = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `MutexGuard<'_, ()>`
note: future is not `Send` as this value is used across an await
  --> src/main.rs:13:10
   |
10 |         let guard = mutex.lock().unwrap();
   |             ----- has type `MutexGuard<'_, ()>` which is not `Send`
...
13 |     nop().await;
   |          ^^^^^^ await occurs here, with `guard` maybe used later
14 | }
   | - `guard` is later dropped here
   = note: required for the cast from `impl Future<Output = ()>` to the object type `dyn Future<Output = ()> + Send`

error: could not compile `playground` due to previous error

When I add the curly braces, the program will compile. The same holds when I use an inner function. (Playground)

Has someone else stumbled upon this problem too? What your usual solution to it?

Thanks to @Yandros for pointing me to the corresponding issues on GitHub:

I guess the best way would be to add curly braces or some inner function to give a hint to the compiler, and maybe add a TODO to remove them when these issues are closed.


In my real-world code, it looks like this:

-    let synced = self.shared.synced.lock();
-    if synced.unseen == 0 && synced.rcvr_count > 0 {
-        break synced;
-    }
-    let notified = self.shared.notify_sndr.notified();
-    drop(synced);
-    notified.await;
+    {
+        let synced = self.shared.synced.lock();
+        if synced.unseen == 0 && synced.rcvr_count > 0 {
+            break synced;
+        }
+        self.shared.notify_sndr.notified()
+    }.await;

The red code fails to compile, the green code compiles.


Oh, I just noticed one of the comments on GitHub mentions the workaround to add braces as well.

That's just how Rust works. The Send check doesn't properly understand drop.

1 Like

So I'll use the braces solution, it seems to work fine now for me.