core: optimize LazyCell
size
This commit is contained in:
parent
8a7ca936e6
commit
f015e6fe49
@ -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.
|
||||
///
|
||||
@ -31,8 +38,7 @@
|
||||
/// ```
|
||||
#[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> {
|
||||
@ -53,8 +59,8 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "lazy_cell", issue = "109736")]
|
||||
pub const fn new(init: F) -> LazyCell<T, F> {
|
||||
LazyCell { cell: OnceCell::new(), init: Cell::new(Some(init)) }
|
||||
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
|
||||
@ -77,10 +83,47 @@ pub const fn new(init: F) -> LazyCell<T, F> {
|
||||
#[inline]
|
||||
#[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"),
|
||||
})
|
||||
let state = unsafe { &*this.state.get() };
|
||||
match state {
|
||||
State::Init(data) => data,
|
||||
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 {
|
||||
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();
|
||||
|
||||
// If the closure accessed the cell, the mutable borrow will be
|
||||
// invalidated, so create a new one here.
|
||||
let state = unsafe { &mut *this.state.get() };
|
||||
*state = State::Init(data);
|
||||
|
||||
// A reference obtained by downcasting from the mutable borrow
|
||||
// would become stale if other references are created in `force`.
|
||||
// Borrow the state directly 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> {
|
||||
let state = unsafe { &*self.state.get() };
|
||||
match state {
|
||||
State::Init(data) => Some(data),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,6 +148,11 @@ fn default() -> LazyCell<T> {
|
||||
#[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()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user