matklad / once_cell

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

Implement OnceCell::get_mut and DerefMut for Lazy #57

Closed alecmocatta closed 5 years ago

alecmocatta commented 5 years ago

Unless there's something I'm missing, if a user has a mutable reference then we can be sure no other references exist, thus these impls would be sound.

impl<T> OnceCell<T> {
    fn get_mut(&mut self) -> Option<&mut T>;
}
impl<T, F: FnOnce() -> T> DerefMut for Lazy<T, F> {
    fn deref_mut(&mut self) -> &mut T;
}

I bumped into the need for this trying to do:

thread_local! {
    static RUNTIME: RefCell<sync::Lazy<Runtime>> = RefCell::new(sync::Lazy::new(|| Runtime::new()));
}

fn some_code() {
    RUNTIME.with(|runtime| {
        runtime.borrow_mut().mutate();
    })
}
matklad commented 5 years ago

OnceCell::get_mut definitely seems like something we should add!

I am not so sure about DerefMut for Lazy. It definitely could work, but I'd like to see more concrete use-cases, as it could confusing as well!

In particular, in your example I think that there's an extra layer of interior mutability: thread_local! in Rust already has "initialized on the first use" semantics. I think what you want is just

thread_local! {
   static RUNTIME: RefCell<Runtime> = Default::default(); // executed lazily, on first acess
}
matklad commented 5 years ago

Note that, once #[thread_local] is stable, the example could be written as

#[thread_local]
static RUNTIME: unsync::Lazy<RefCell<Runtime>> = unsync::Lazy::new(Default::default);
alecmocatta commented 5 years ago

Thanks! Good point, I had a feeling my example betrayed my lack of familiarity with thread_local!

I will bear it in mind and make a note whenever I come across a use case for DerefMut on Lazy.

matklad commented 5 years ago

Added get_mut and published as 1.1.0.

Let's close this for know, feel free to repopen if you still feel that DerfefMut is needed!