Skip to content

Commit c7f9739

Browse files
committed
core: improve code documentation for LazyCell
1 parent f015e6f commit c7f9739

File tree

1 file changed

+24
-5
lines changed

1 file changed

+24
-5
lines changed

library/core/src/cell/lazy.rs

+24-5
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,15 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
8383
#[inline]
8484
#[unstable(feature = "lazy_cell", issue = "109736")]
8585
pub fn force(this: &LazyCell<T, F>) -> &T {
86+
// SAFETY:
87+
// This invalidates any mutable references to the data. The resulting
88+
// reference lives either until the end of the borrow of `this` (in the
89+
// initialized case) or is invalidates in `really_init` (in the
90+
// uninitialized case).
8691
let state = unsafe { &*this.state.get() };
8792
match state {
8893
State::Init(data) => data,
94+
// SAFETY: The state is uninitialized.
8995
State::Uninit(_) => unsafe { LazyCell::really_init(this) },
9096
State::Poisoned => panic!("LazyCell has previously been poisoned"),
9197
}
@@ -95,21 +101,30 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
95101
/// May only be called when the state is `Uninit`.
96102
#[cold]
97103
unsafe fn really_init(this: &LazyCell<T, F>) -> &T {
104+
// SAFETY:
105+
// This function is only called when the state is uninitialized,
106+
// so no references to `state` can exist except for the reference
107+
// in `force`, which is invalidated here and not accessed again.
98108
let state = unsafe { &mut *this.state.get() };
99109
// Temporarily mark the state as poisoned. This prevents reentrant
100110
// accesses and correctly poisons the cell if the closure panicked.
101111
let State::Uninit(f) = mem::replace(state, State::Poisoned) else { unreachable!() };
102112

103113
let data = f();
104114

105-
// If the closure accessed the cell, the mutable borrow will be
106-
// invalidated, so create a new one here.
115+
// SAFETY:
116+
// If the closure accessed the cell through something like a reentrant
117+
// mutex, but caught the panic resulting from the state being poisoned,
118+
// the mutable borrow for `state` will be invalidated, so create a new
119+
// one here.
107120
let state = unsafe { &mut *this.state.get() };
108121
*state = State::Init(data);
109122

110-
// A reference obtained by downcasting from the mutable borrow
111-
// would become stale if other references are created in `force`.
112-
// Borrow the state directly instead.
123+
// SAFETY:
124+
// A reference obtained by downcasting from the mutable borrow would
125+
// become stale the next time `force` is called (since there is a conflict
126+
// between the mutable reference here and the shared reference there).
127+
// Do a new shared borrow of the state instead.
113128
let state = unsafe { &*this.state.get() };
114129
let State::Init(data) = state else { unreachable!() };
115130
data
@@ -119,6 +134,10 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
119134
impl<T, F> LazyCell<T, F> {
120135
#[inline]
121136
fn get(&self) -> Option<&T> {
137+
// SAFETY:
138+
// This is sound for the same reason as in `force`: once the state is
139+
// initialized, it will not be mutably accessed again, so this reference
140+
// will stay valid for the duration of the borrow to `self`.
122141
let state = unsafe { &*self.state.get() };
123142
match state {
124143
State::Init(data) => Some(data),

0 commit comments

Comments
 (0)