0

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);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
arik
  • 28,170
  • 36
  • 100
  • 156
  • 1
    "The static FOO_BAR variable is later __initialized__ by replacing the content of the option", affected. You want use an `Arc` but you want the ownership of your data, but you can't clone... that impossible, what wrong with the reference ? – Stargateur Feb 03 '19 at 11:19
  • The duplicates applied to your question: `FOO_BAR.lock().unwrap().take()` – Shepmaster Feb 03 '19 at 14:04
  • @Stargateur a reference would be perfectly fine, but returning a reference makes the compiler complain about it not living long enough. Whereas using `take()` will only make the contained struct usable once, though the point of this exercise would be making it available as many times as necessary, like a singleton, after initialization. – arik Feb 03 '19 at 18:12
  • AFAIK, that impossible. – Stargateur Feb 03 '19 at 21:07
  • *`pub fn unpack_foo_bar() -> Option` ... I don't care if it's owned* — these two statements are in direct contradiction. You **cannot** have this signature unless you take ownership. – Shepmaster Feb 03 '19 at 21:32
  • Perhaps [How to return a reference to a sub-value of a value that is under a mutex?](https://stackoverflow.com/q/40095383/155423) / [Returning a RWLockReadGuard independently from a method](https://stackoverflow.com/q/50496879/155423) is more what you are looking for? – Shepmaster Feb 03 '19 at 21:34
  • *`fn unpack_foo_bar() -> &Option`* — [Is there any way to return a reference to a variable created in a function?](https://stackoverflow.com/q/32682876/155423) – Shepmaster Feb 03 '19 at 21:38
  • Thanks for your comments, @Shepmaster. I had seen the sub-value/value question before. Given all the other information in the other questions you linked to, I think the best way for me to proceed is, rather than bringing the FooBar reference to the methods that need them, instead bringing those methods to where I handle the mutex in the form of closures. – arik Feb 04 '19 at 07:50
  • @arik what's wrong with returning something using the rental crate or owning_ref crate? That would return a (locked) value that can be treated as a reference to the `Option`. – Shepmaster Feb 04 '19 at 14:44
  • It just seemed like too much additional headache for the time being. But I'll figure it out eventually. – arik Feb 04 '19 at 21:29

0 Answers0