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();

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



   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.

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.