Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Move `Var` out from `Arc<Mutex<Var>>`

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.

like image 427
Rahn Avatar asked Dec 13 '21 10:12

Rahn


1 Answers

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{})
like image 123
rodrigo Avatar answered Sep 19 '22 09:09

rodrigo