use std::ops::Deref;
use std::sync::{Arc, Mutex, MutexGuard};
struct Var {}
fn multithreading() -> Var {
let shared_var = Arc::new(Mutex::new(Var {}));
/*
multithreading job
*/
return *(shared_var.lock().unwrap().deref());
}
I'm defining a multi-threading function to operate on Var
but this function doesn't compile and complaint:
error[E0507]: cannot move out of a shared reference
Is there any way to stop the sharing of shared_var
and return the variable within?
Implementing trait Copy
for Var
may also solve the bug, but in my actual use case Var
is too large to copy that I would prefer any other solution.
CodePudding user response:
The problem here is that if you remove your Var
from the shared variable, what would be left there? What happens if any other copy of your Arc
is left somewhere and it tries to access the now removed object?
There are several possible answers to that question:
1. I'm positively sure there is no other strong reference, this is the last Arc
. If not, let it panic.
If that is the case, you can use Arc::try_unwrap()
to get to the inner mutex. Then another into_inner()
to get the real value.
let mutex = Arc::try_unwrap(shared_var).unwrap();
mutex.into_inner().unwrap()
Note that for these Result::unwrap()
to work your type has to implement Debug
, for... reasons. If it does not, you can use a match/panic!
pair.
2. There may be other strong referenced copies of Arc
, I want to steal the object anyway.
Then you have to put something in its place. The obvious solution is to store an Option<Var>
instead and use Option::take()
to steal the value:
let shared_var = Arc::new(Mutex::new(Some(Var {})));
/*
multithreading job
*/
let mut lck = shared_var.lock().unwrap();
lck.take().unwrap()
Now, any other use of this shared value has to check the option in case there is a None
there.
3. There may be other copies of Arc
but I don't want to deal with the Option<Var>
.
But you must leave something there! Maybe you can insert a dummy object in its place. If your Var
implements Default
you can use std::mem::take()
:
let mut lck = shared_var.lock().unwrap();
std::mem::take(&mut *lck)
If it does not implement Default
but you are able to create an empty object cheapily, use std::mem::replace()
instead:
let mut lck = shared_var.lock().unwrap();
std::mem::replace(&mut *lck, Var{})