2

I'd like to share a struct with an Arc<Mutex<Fn(i64)>> through a thread. I've minimally reproduced the problem below:

use std::sync::{Arc, Mutex};

struct NonSendable {
    problematic: Arc<Mutex<Fn(i64)>>,
}

fn main() {
    let bad = NonSendable {
        problematic: Arc::new(Mutex::new(|i| println!("{}", i))),
    };

    std::thread::spawn(|| {
        for i in 0..10 {
            let f = bad.problematic.lock().unwrap();
            f(i);
        }
    });
}

However, this errors on compilation.

error[E0277]: `(dyn std::ops::Fn(i64) + 'static)` cannot be sent between threads safely
  --> src/main.rs:13:5
   |
13 |     std::thread::spawn(|| {
   |     ^^^^^^^^^^^^^^^^^^ `(dyn std::ops::Fn(i64) + 'static)` cannot be sent between threads safely
   |
   = help: the trait `std::marker::Send` is not implemented for `(dyn std::ops::Fn(i64) + 'static)`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Mutex<(dyn std::ops::Fn(i64) + 'static)
>`
   = note: required because of the requirements on the impl of `std::marker::Sync` for `std::sync::Arc<std::sync::Mutex<(dyn std::ops::Fn(
i64) + 'static)>>`
   = note: required because it appears within the type `NonSendable`
   = note: required because of the requirements on the impl of `std::marker::Send` for `&NonSendable`
   = note: required because it appears within the type `[closure@src/main.rs:13:24: 18:6 bad:&NonSendable]`
   = note: required by `std::thread::spawn`

error: aborting due to previous error

I guess I might understand why a Fn isn't Send or Sync, but should a Mutex not cover that issue? I've also tried to Box the Fn and use FnMut instead.

The end goal here is to have a Vec of closures that can be accessed and modified in one thread while read in another thread, so I think I need multiple non-mut references (or maybe an Arc) to coexist in different threads with an Arc<Mutex<Vec<Fn>>> or similar as a member, but if I can't get the above to work I'll have to use a different strategy.

River Tam
  • 3,096
  • 4
  • 31
  • 51

1 Answers1

0

Figured it out very shortly after I asked (though I'd been thinking about it for a day).

Fn is just a trait, which itself does not add Send. However, closures are also Send, so I need to replace Mutex<Fn(i64)> with Mutex<Fn(i64) + Send>. This reveals other problems, but solves the original one.

River Tam
  • 3,096
  • 4
  • 31
  • 51
  • It occurs to me that, while this fixes the code, it doesn't really explain to me why `Mutex` wouldn't be `Send`. I'd be interested to hear an answer that also explains this. – River Tam Dec 05 '18 at 03:54
  • 1
    That second part is answered [here](https://stackoverflow.com/q/52866447/1233251). `Mutex` does not automatically make something safe to use in a multi-threaded environment. – E_net4 Dec 05 '18 at 08:59