I'm experimenting with Rust's Arc<Mutex>
, trying to use it with a struct that is not in the default examples, but rather a custom one that is lazily initialized:
use std::sync::{Arc, Mutex};
#[macro_use]
extern crate lazy_static; // lazy_static = "1.2.0"
pub struct FooBar {}
lazy_static! {
static ref FOO_BAR: Arc<Mutex<Option<FooBar>>> = Arc::new(Mutex::new(None));
}
pub fn unpack_foo_bar() {
let foo_bar_arc = Arc::clone(&FOO_BAR);
let foo_bar_mutex_result = foo_bar_arc.lock();
let foo_bar_mutex = foo_bar_mutex_result.unwrap();
let foo_bar = foo_bar_mutex.unwrap();
// do something
}
The static FOO_BAR
variable is later initialized by replacing the content of the option.
The code above won't compile:
error[E0507]: cannot move out of borrowed content
--> src/lib.rs:15:19
|
15 | let foo_bar = foo_bar_mutex.unwrap();
| ^^^^^^^^^^^^^ cannot move out of borrowed content
It would if FooBar
were replaced with e. g. u32
. The code also doesn't compile with String
, but that type has built-in methods for cloning, which my type may not necessarily have.
Short of using foo_bar_mutex.as_ref().unwrap()
, what other options do I have, particularly if I'd like to abstract the extraction of the FooBar
instance into a method, like this:
pub fn unpack_foo_bar() -> Option<FooBar> {
let foo_bar_arc = Arc::clone(&FOO_BAR);
let foo_bar_mutex_result = foo_bar_arc.lock();
let foo_bar_mutex = foo_bar_mutex_result.unwrap();
let foo_bar_option = *foo_bar_mutex;
foo_bar_option
}
In this case, the compiler throws almost the same error, which is
error[E0507]: cannot move out of borrowed content
--> src/main.rs:34:26
|
11 | let foo_bar_option = *foo_bar_mutex;
| ^^^^^^^^^^^^^^
| |
| cannot move out of borrowed content
| help: consider using a reference instead: `&*foo_bar_mutex`
I have a sense that this would be much simpler if FooBar
were easily cloned, but in the real world scenario I'm basing this example on FooBar
has a field that is an instance of a third party library object that does not derive the Clone
trait.
Using Option::take
would only make the contained variable usable once. I'm not trying to take ownership of the FooBar
instance. I don't care if it's owned as long as I can call its methods. Returning a reference would be great, but when doing this, the compiler complains:
pub fn unpack_foo_bar() -> &Option<FooBar> {
let foo_bar_arc = Arc::clone(&FOO_BAR);
let foo_bar_mutex_result = foo_bar_arc.lock();
let foo_bar_mutex = foo_bar_mutex_result.unwrap();
let foo_bar_option = foo_bar_mutex.as_ref();
foo_bar_option
}
Compiler's response:
error[E0106]: missing lifetime specifier
--> src/main.rs:30:28
|
7 | pub fn unpack_foo_bar() -> &Option<FooBar> {
| ^ help: consider giving it a 'static lifetime: `&'static`
|
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
Experimenting with &*foo_bar_mutex
and adding 'static
to the return type each opens up subsequent cans of compiler errors.
I have also tried experimenting with to_owned()
on the FooBar
reference and stumbled upon the owning_ref crate, but alas, haven't figured out how to make returning a reference work.
Final Update
Given that it appears impossible to pass on the reference to the Option<FooBar>
to an external function caller, I decided to avoid this problem altogether by allowing passing in the methods relying on the FooBar
instance as a closure:
pub fn unpack_foo_bar(use_foo_bar: fn(&FooBar)) {
let foo_bar_arc = Arc::clone(&FOO_BAR);
let foo_bar_mutex_result = foo_bar_arc.lock();
let foo_bar_mutex = foo_bar_mutex_result.unwrap();
let foo_bar_reference = foo_bar_mutex.as_ref().unwrap();
// pass in the closure that needs the foo bar instance
use_foo_bar(foo_bar_reference);
}