I'm pretty sure you can't do this due to the mutable aliasing guarantees in Rust. In Rust you can't have two mutable references to the same thing at the same time, but this is exactly what happens in your code: you store my_facade
to the field of Client
and at the same time you are trying to pass it to start()
method in another thread. This would require having two mutable references to the same Facade
which is disallowed.
The actual errors which compiler emits on your code are caused by that you're using a non-moving closure. If you change thread::scoped()
instantiation to this:
join_grd: thread::scoped(move || Client::start(my_facade))
the error would be more sensible:
test.rs:16:60: 16:69 error: cannot move `my_facade` into closure because it is borrowed
test.rs:16 join_grd: thread::scoped(move || Client::start(my_facade))
^~~~~~~~~
test.rs:15:21: 15:30 note: borrow of `*my_facade` occurs here
test.rs:15 facade: my_facade,
^~~~~~~~~
This essentially means that since &mut
references are unique and are moved instead of copied, you can't duplicate them. Similar code with the regular &
reference instead of &mut
(and an additional Sync
parent trait on Facade
) works fine.
You have to rethink your architecture to fix this error. It is difficult to understand what you want from this piece of code alone, so I can't give any exact advices, but you may consider using Arc
and Mutex
if you want to share mutable state between threads.
Naive usage of Arc/Mutex
like this:
fn start(my_facade: Arc<Mutex<Facade>>)
won't work because Facade
is a trait, not a regular type. When you use traits as types, you're in fact opting into dynamic dispatch in form of trait objects. In short, trait objects can't be used directly; they should always be behind a pointer. Your original program also used trait objects (&'a mut Facade
is a trait object). Ideally we should be able to form trait objects with any kind of smart pointer, and ideally Arc<Mutex<Facade>>
should work, but unfortunately for now trait objects can only be created with &
, &mut
or Box
:
fn start(my_facade: Arc<Mutex<Box<Facade>>>)
This is the reason of the error about Sized
that you observe.
However, you should also consider not using trait objects at all and just use generics:
trait Facade: Send { fn f(&self); }
struct Client<'a, F: Facade> { // '
facade: Arc<Mutex<F>>,
join_grd: thread::JoinGuard<'a, ()>, // '
}
impl<'a, F: Facade+'a> Client<'a, F> { // '
pub fn new(my_facade: Arc<Mutex<F>>) -> Client<'a, F> { // '
let my_facade_2 = my_facade.clone(); // clone the Arc pointer
Client {
facade: my_facade,
join_grd: thread::scoped(move || Client::start(my_facade_2)),
}
}
fn start(my_facade: Arc<Mutex<F>>) { unimplemented!() }
}
You also need to add Send
bound either on the trait itself (as in the example above) or on F
type variable (as in F: Facade+Send+'a
) because only Send
data may be transferred between threads safely, so you need to specify that F
is Send
, either directly or as a supertrait constraint on Facade
.