summaryrefslogtreecommitdiffstats
path: root/library/core/src/cell
diff options
context:
space:
mode:
Diffstat (limited to 'library/core/src/cell')
-rw-r--r--library/core/src/cell/lazy.rs106
-rw-r--r--library/core/src/cell/once.rs52
2 files changed, 109 insertions, 49 deletions
diff --git a/library/core/src/cell/lazy.rs b/library/core/src/cell/lazy.rs
index 65d12c25c..44adcfa1a 100644
--- a/library/core/src/cell/lazy.rs
+++ b/library/core/src/cell/lazy.rs
@@ -1,6 +1,13 @@
-use crate::cell::{Cell, OnceCell};
-use crate::fmt;
use crate::ops::Deref;
+use crate::{fmt, mem};
+
+use super::UnsafeCell;
+
+enum State<T, F> {
+ Uninit(F),
+ Init(T),
+ Poisoned,
+}
/// A value which is initialized on the first access.
///
@@ -11,7 +18,7 @@ use crate::ops::Deref;
/// # Examples
///
/// ```
-/// #![feature(once_cell)]
+/// #![feature(lazy_cell)]
///
/// use std::cell::LazyCell;
///
@@ -29,10 +36,9 @@ use crate::ops::Deref;
/// // 92
/// // 92
/// ```
-#[unstable(feature = "once_cell", issue = "74465")]
+#[unstable(feature = "lazy_cell", issue = "109736")]
pub struct LazyCell<T, F = fn() -> T> {
- cell: OnceCell<T>,
- init: Cell<Option<F>>,
+ state: UnsafeCell<State<T, F>>,
}
impl<T, F: FnOnce() -> T> LazyCell<T, F> {
@@ -41,7 +47,7 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
/// # Examples
///
/// ```
- /// #![feature(once_cell)]
+ /// #![feature(lazy_cell)]
///
/// use std::cell::LazyCell;
///
@@ -52,9 +58,9 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
/// assert_eq!(&*lazy, "HELLO, WORLD!");
/// ```
#[inline]
- #[unstable(feature = "once_cell", issue = "74465")]
- pub const fn new(init: F) -> LazyCell<T, F> {
- LazyCell { cell: OnceCell::new(), init: Cell::new(Some(init)) }
+ #[unstable(feature = "lazy_cell", issue = "109736")]
+ pub const fn new(f: F) -> LazyCell<T, F> {
+ LazyCell { state: UnsafeCell::new(State::Uninit(f)) }
}
/// Forces the evaluation of this lazy value and returns a reference to
@@ -65,7 +71,7 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
/// # Examples
///
/// ```
- /// #![feature(once_cell)]
+ /// #![feature(lazy_cell)]
///
/// use std::cell::LazyCell;
///
@@ -75,16 +81,71 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
/// assert_eq!(&*lazy, &92);
/// ```
#[inline]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[unstable(feature = "lazy_cell", issue = "109736")]
pub fn force(this: &LazyCell<T, F>) -> &T {
- this.cell.get_or_init(|| match this.init.take() {
- Some(f) => f(),
- None => panic!("`Lazy` instance has previously been poisoned"),
- })
+ // SAFETY:
+ // This invalidates any mutable references to the data. The resulting
+ // reference lives either until the end of the borrow of `this` (in the
+ // initialized case) or is invalidated in `really_init` (in the
+ // uninitialized case; `really_init` will create and return a fresh reference).
+ let state = unsafe { &*this.state.get() };
+ match state {
+ State::Init(data) => data,
+ // SAFETY: The state is uninitialized.
+ State::Uninit(_) => unsafe { LazyCell::really_init(this) },
+ State::Poisoned => panic!("LazyCell has previously been poisoned"),
+ }
+ }
+
+ /// # Safety
+ /// May only be called when the state is `Uninit`.
+ #[cold]
+ unsafe fn really_init(this: &LazyCell<T, F>) -> &T {
+ // SAFETY:
+ // This function is only called when the state is uninitialized,
+ // so no references to `state` can exist except for the reference
+ // in `force`, which is invalidated here and not accessed again.
+ let state = unsafe { &mut *this.state.get() };
+ // Temporarily mark the state as poisoned. This prevents reentrant
+ // accesses and correctly poisons the cell if the closure panicked.
+ let State::Uninit(f) = mem::replace(state, State::Poisoned) else { unreachable!() };
+
+ let data = f();
+
+ // SAFETY:
+ // If the closure accessed the cell through something like a reentrant
+ // mutex, but caught the panic resulting from the state being poisoned,
+ // the mutable borrow for `state` will be invalidated, so we need to
+ // go through the `UnsafeCell` pointer here. The state can only be
+ // poisoned at this point, so using `write` to skip the destructor
+ // of `State` should help the optimizer.
+ unsafe { this.state.get().write(State::Init(data)) };
+
+ // SAFETY:
+ // The previous references were invalidated by the `write` call above,
+ // so do a new shared borrow of the state instead.
+ let state = unsafe { &*this.state.get() };
+ let State::Init(data) = state else { unreachable!() };
+ data
+ }
+}
+
+impl<T, F> LazyCell<T, F> {
+ #[inline]
+ fn get(&self) -> Option<&T> {
+ // SAFETY:
+ // This is sound for the same reason as in `force`: once the state is
+ // initialized, it will not be mutably accessed again, so this reference
+ // will stay valid for the duration of the borrow to `self`.
+ let state = unsafe { &*self.state.get() };
+ match state {
+ State::Init(data) => Some(data),
+ _ => None,
+ }
}
}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[unstable(feature = "lazy_cell", issue = "109736")]
impl<T, F: FnOnce() -> T> Deref for LazyCell<T, F> {
type Target = T;
#[inline]
@@ -93,7 +154,7 @@ impl<T, F: FnOnce() -> T> Deref for LazyCell<T, F> {
}
}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[unstable(feature = "lazy_cell", issue = "109736")]
impl<T: Default> Default for LazyCell<T> {
/// Creates a new lazy value using `Default` as the initializing function.
#[inline]
@@ -102,9 +163,14 @@ impl<T: Default> Default for LazyCell<T> {
}
}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[unstable(feature = "lazy_cell", issue = "109736")]
impl<T: fmt::Debug, F> fmt::Debug for LazyCell<T, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish()
+ let mut d = f.debug_tuple("LazyCell");
+ match self.get() {
+ Some(data) => d.field(data),
+ None => d.field(&format_args!("<uninit>")),
+ };
+ d.finish()
}
}
diff --git a/library/core/src/cell/once.rs b/library/core/src/cell/once.rs
index f74e563f1..f7cd3ec5f 100644
--- a/library/core/src/cell/once.rs
+++ b/library/core/src/cell/once.rs
@@ -4,8 +4,10 @@ use crate::mem;
/// A cell which can be written to only once.
///
-/// Unlike [`RefCell`], a `OnceCell` only provides shared `&T` references to its value.
-/// Unlike [`Cell`], a `OnceCell` doesn't require copying or replacing the value to access it.
+/// This allows obtaining a shared `&T` reference to its inner value without copying or replacing
+/// it (unlike [`Cell`]), and without runtime borrow checks (unlike [`RefCell`]). However,
+/// only immutable references can be obtained unless one has a mutable reference to the cell
+/// itself.
///
/// For a thread-safe version of this struct, see [`std::sync::OnceLock`].
///
@@ -16,8 +18,6 @@ use crate::mem;
/// # Examples
///
/// ```
-/// #![feature(once_cell)]
-///
/// use std::cell::OnceCell;
///
/// let cell = OnceCell::new();
@@ -29,7 +29,7 @@ use crate::mem;
/// assert_eq!(value, "Hello, World!");
/// assert!(cell.get().is_some());
/// ```
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
pub struct OnceCell<T> {
// Invariant: written to at most once.
inner: UnsafeCell<Option<T>>,
@@ -39,7 +39,8 @@ impl<T> OnceCell<T> {
/// Creates a new empty cell.
#[inline]
#[must_use]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[stable(feature = "once_cell", since = "1.70.0")]
+ #[rustc_const_stable(feature = "once_cell", since = "1.70.0")]
pub const fn new() -> OnceCell<T> {
OnceCell { inner: UnsafeCell::new(None) }
}
@@ -48,7 +49,7 @@ impl<T> OnceCell<T> {
///
/// Returns `None` if the cell is empty.
#[inline]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[stable(feature = "once_cell", since = "1.70.0")]
pub fn get(&self) -> Option<&T> {
// SAFETY: Safe due to `inner`'s invariant
unsafe { &*self.inner.get() }.as_ref()
@@ -58,7 +59,7 @@ impl<T> OnceCell<T> {
///
/// Returns `None` if the cell is empty.
#[inline]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[stable(feature = "once_cell", since = "1.70.0")]
pub fn get_mut(&mut self) -> Option<&mut T> {
self.inner.get_mut().as_mut()
}
@@ -73,8 +74,6 @@ impl<T> OnceCell<T> {
/// # Examples
///
/// ```
- /// #![feature(once_cell)]
- ///
/// use std::cell::OnceCell;
///
/// let cell = OnceCell::new();
@@ -86,7 +85,7 @@ impl<T> OnceCell<T> {
/// assert!(cell.get().is_some());
/// ```
#[inline]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[stable(feature = "once_cell", since = "1.70.0")]
pub fn set(&self, value: T) -> Result<(), T> {
// SAFETY: Safe because we cannot have overlapping mutable borrows
let slot = unsafe { &*self.inner.get() };
@@ -117,8 +116,6 @@ impl<T> OnceCell<T> {
/// # Examples
///
/// ```
- /// #![feature(once_cell)]
- ///
/// use std::cell::OnceCell;
///
/// let cell = OnceCell::new();
@@ -128,7 +125,7 @@ impl<T> OnceCell<T> {
/// assert_eq!(value, &92);
/// ```
#[inline]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[stable(feature = "once_cell", since = "1.70.0")]
pub fn get_or_init<F>(&self, f: F) -> &T
where
F: FnOnce() -> T,
@@ -153,7 +150,7 @@ impl<T> OnceCell<T> {
/// # Examples
///
/// ```
- /// #![feature(once_cell)]
+ /// #![feature(once_cell_try)]
///
/// use std::cell::OnceCell;
///
@@ -166,7 +163,7 @@ impl<T> OnceCell<T> {
/// assert_eq!(value, Ok(&92));
/// assert_eq!(cell.get(), Some(&92))
/// ```
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[unstable(feature = "once_cell_try", issue = "109737")]
pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E>
where
F: FnOnce() -> Result<T, E>,
@@ -199,8 +196,6 @@ impl<T> OnceCell<T> {
/// # Examples
///
/// ```
- /// #![feature(once_cell)]
- ///
/// use std::cell::OnceCell;
///
/// let cell: OnceCell<String> = OnceCell::new();
@@ -211,7 +206,7 @@ impl<T> OnceCell<T> {
/// assert_eq!(cell.into_inner(), Some("hello".to_string()));
/// ```
#[inline]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[stable(feature = "once_cell", since = "1.70.0")]
pub fn into_inner(self) -> Option<T> {
// Because `into_inner` takes `self` by value, the compiler statically verifies
// that it is not currently borrowed. So it is safe to move out `Option<T>`.
@@ -227,8 +222,6 @@ impl<T> OnceCell<T> {
/// # Examples
///
/// ```
- /// #![feature(once_cell)]
- ///
/// use std::cell::OnceCell;
///
/// let mut cell: OnceCell<String> = OnceCell::new();
@@ -240,13 +233,13 @@ impl<T> OnceCell<T> {
/// assert_eq!(cell.get(), None);
/// ```
#[inline]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[stable(feature = "once_cell", since = "1.70.0")]
pub fn take(&mut self) -> Option<T> {
mem::take(self).into_inner()
}
}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
impl<T> Default for OnceCell<T> {
#[inline]
fn default() -> Self {
@@ -254,7 +247,7 @@ impl<T> Default for OnceCell<T> {
}
}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
impl<T: fmt::Debug> fmt::Debug for OnceCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.get() {
@@ -264,7 +257,7 @@ impl<T: fmt::Debug> fmt::Debug for OnceCell<T> {
}
}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
impl<T: Clone> Clone for OnceCell<T> {
#[inline]
fn clone(&self) -> OnceCell<T> {
@@ -279,7 +272,7 @@ impl<T: Clone> Clone for OnceCell<T> {
}
}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
impl<T: PartialEq> PartialEq for OnceCell<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
@@ -287,10 +280,11 @@ impl<T: PartialEq> PartialEq for OnceCell<T> {
}
}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
impl<T: Eq> Eq for OnceCell<T> {}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
+#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
impl<T> const From<T> for OnceCell<T> {
/// Creates a new `OnceCell<T>` which already contains the given `value`.
#[inline]
@@ -300,5 +294,5 @@ impl<T> const From<T> for OnceCell<T> {
}
// Just like for `Cell<T>` this isn't needed, but results in nicer error messages.
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
impl<T> !Sync for OnceCell<T> {}