Search code examples
rustlazy-initialization

not able to get mutable reference to the underlying OnceLock data


I'm trying to get a mutable reference to the underlying data of OnceLock using get_mut() method, but getting error

mod OL {
    use std::sync::OnceLock;
    
    static CELL: OnceLock<i32> = OnceLock::new();
    
    pub fn set_cell_val(val: i32) {
        CELL.set(val);
    }
    
    pub fn get_mut_ref() -> &'static mut i32 {
        CELL.get_mut().unwrap()
    }
}

fn main() {
    println!("Hello, world!");
}

Error

error[E0596]: cannot borrow immutable static item `CELL` as mutable
  --> src/main.rs:11:9
   |
11 |         CELL.get_mut().unwrap()
   |         ^^^^^^^^^^^^^^ cannot borrow as mutable

For more information about this error, try `rustc --explain E0596`.

Solution

  • OnceLock is a cell that is assigned once. By definition, after being assigned it can no longer change.

    get_mut() does exist, but it serves niche use cases and definitely not yours. OnceLock is just not for you.

    What you want is a Mutex, or a RwLock. They can be changed multiple times:

    use std::sync::{Mutex, MutexGuard};
    
    static VALUE: Mutex<i32> = Mutex::new(0);
    
    pub fn set_value(val: i32) {
        *value.lock().unwrap() = val;
    }
    
    // This is not a mutable reference, but it acts like one (e.g. it can be assigned into).
    pub fn get_mut_ref() -> MutexGuard<'static, i32> {
        VALUE.lock().unwrap()
    }
    

    If you want a changing value that is also lazily-initialized, you can use OnceLock<Mutex>.