matklad / once_cell

Rust library for single assignment cells and lazy statics without macros
Apache License 2.0
1.84k stars 110 forks source link

how to get Mutex inner variable in CONFIG in once_cell #117

Closed baoyachi closed 3 years ago

baoyachi commented 3 years ago

how to get reference Ext in CONFIG

use once_cell::sync::Lazy;
use std::sync::Mutex;
use std::collections::HashMap;
use std::fmt::Debug;

trait IExt: Debug + Send + Sync {
    fn get_key(&self);
}

#[derive(Debug)]
struct Ext;

impl IExt for Ext {
    fn get_key(&self) {}
}

static CONFIG: Lazy<Mutex<HashMap<String, Box<dyn IExt>>>> = Lazy::new(|| Mutex::new(Default::default()));

fn add() {
    CONFIG.lock().unwrap().insert("1".to_string(), Box::new(Ext));
}

fn get_key<'a>() -> &'a Box<dyn IExt> {
    CONFIG.lock().unwrap().get("1").unwrap()
}

fn main() {
    add();
    let key = get_key();
    println!("{:?}", key);
}

compile error:

error[E0515]: cannot return value referencing temporary value
  --> src/main.rs:27:5
   |
27 |     CONFIG.lock().unwrap().get("1").unwrap()
   |     ----------------------^^^^^^^^^^^^^^^^^^
   |     |
   |     returns a value referencing data owned by the current function
   |     temporary value created here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0515`.
error: could not compile `rustls-example`.

To learn more, run the command again with --verbose.

Process finished with exit code 101
matklad commented 3 years ago

That's not possible, because that would be unsfae.You are trying to return a pointer inside the value, protected by the mutex, but the pointer is not protected by a lock guard. That means that some other thread could sucesfully lock the mutext and mutate the protected data, invalidating the pointer. You need to either clone the data (using Arc<dyn IExt> would help) or use something like map from parking lot: https://docs.rs/lock_api/0.4.0/lock_api/struct.MutexGuard.html#method.map