I'm using a trait which isn't designed around multithreading (Cursive).
Now, while it's using multithreading, it's going to be behind a mutex, so it won't be able to be used at two threads at the same time.
What is rust trying to protect me against and can I do anything about it?
For sample reference, my sample code is:
extern crate cursive;
use cursive::Cursive;
use std::thread;
use std::sync::{Mutex,Arc};
fn main() {
let mut siv = Arc::new(Mutex::new(Cursive::default()));
let copy_siv = siv.clone();
thread::spawn(move || {
let mut new_siv = copy_siv.lock().unwrap();
});
(*(siv.lock().unwrap())).run();
}
The compiler complains at thread::spawn
:
Error[E0277]: `(dyn cursive::traits::View + 'static)` cannot be sent between threads safely
--> src/main.rs:16:5
|
16 | thread::spawn(move || {
| ^^^^^^^^^^^^^ `(dyn cursive::traits::View + 'static)` cannot be sent between threads safely
|
= help: the trait `std::marker::Send` is not implemented for `(dyn cursive::traits::View + 'static)`
Something in what you're sending between threads contains a
dyn cursive::traits::View
trait object. This trait object is notSend
. It needs to beSend
because by putting it inside anArc
, you can no longer predict which thread will be responsible for destroying it, so it must be safe to transfer ownership between threads.You haven't provided enough context to say for certain, but probably not.
You could maybe try using a plain borrowed reference (plus a threading library that supports scoped threads), but I can't say if that will work for you.
No. It can't make something thread-safe when it wasn't already thread-safe.
Mutex
just manages exclusive access to a value, it doesn't make that access from different threads safe. The only thing that can make a type thread-safe is the type in question.Making a guess: the library was written such that it does not require thread safety, thus
Arc
cannot assume it's thread-safe, so it refuses to compile.I don't know what your actual code is. But the following example replicate the exact error you have:
You can try it in playground. The error message:
Analysis and Solution
The error message explained everything to experienced users. For those new to the language,
siv
is a reference counted, mutex protected trait object. This object only known to be aView
, the compiler have no evidence on whether or not it isSend
. However, for the code to work,Arc<Mutex<T>>
must beSend
, as you are sending such a thing to another thread; Therefore:Mutex<T>
must beSend
andSync
, asArc
requires the reference counted object to beSend
andSync
. Therefore:T
must beSend
, as the same object will be accessed in different threads without any further protection.So, this code does not work. The solution is
You can try it yourself!
Mutex<T>: Send + Sync
requiresT: Send
To see why, first ask a question: what cannot be
Send
?One example is that references to things with interior mutablity cannot be
Send
. Because if they were, people can mutate the thing through interior mutability in different threads and causes data race.Now suppose you have a
Mutex<&Cell<T>>
, because the protected thing is only a reference, not theCell
itself, theCell
itself may still be somewhere unprotected. The compiler thus cannot conclude when you calllock().set()
there is no risk to cause data race. So the compiler prevent it fromSend
.What if I have to ...
So we see that
&Cell<T>
is notSend
, and so even it is protected inMutex
we still cannot use it in another thread. What can we do then?This problem is actually not new. Almost all UI API have the same problem: the UI components were created in the UI thread, and so you cannot access them in any other threads. Instead, you have to schedule a routine to be run in the UI thread, and let the UI thread to access the component.
Fails to do so in other languages (.NET, Java...) will throw exceptions in the best, causing undefined behavior in the worst. Once again, Rust turns such violates into compile errors without special treatments (
&Cell<T>
have nothing to do with UI), this is really GOOD!So, if this is what you wanted to do, you have to do the same thing: access the view object in the UI thread only. How to do so depends on the API you were using.