1- use crate :: cell:: { Cell , OnceCell } ;
2- use crate :: fmt;
31use crate :: ops:: Deref ;
2+ use crate :: { fmt, mem} ;
3+
4+ use super :: UnsafeCell ;
5+
6+ enum State < T , F > {
7+ Uninit ( F ) ,
8+ Init ( T ) ,
9+ Poisoned ,
10+ }
411
512/// A value which is initialized on the first access.
613///
@@ -31,8 +38,7 @@ use crate::ops::Deref;
3138/// ```
3239#[ unstable( feature = "lazy_cell" , issue = "109736" ) ]
3340pub struct LazyCell < T , F = fn ( ) -> T > {
34- cell : OnceCell < T > ,
35- init : Cell < Option < F > > ,
41+ state : UnsafeCell < State < T , F > > ,
3642}
3743
3844impl < T , F : FnOnce ( ) -> T > LazyCell < T , F > {
@@ -53,8 +59,8 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
5359 /// ```
5460 #[ inline]
5561 #[ unstable( feature = "lazy_cell" , issue = "109736" ) ]
56- pub const fn new ( init : F ) -> LazyCell < T , F > {
57- LazyCell { cell : OnceCell :: new ( ) , init : Cell :: new ( Some ( init ) ) }
62+ pub const fn new ( f : F ) -> LazyCell < T , F > {
63+ LazyCell { state : UnsafeCell :: new ( State :: Uninit ( f ) ) }
5864 }
5965
6066 /// Forces the evaluation of this lazy value and returns a reference to
@@ -77,10 +83,47 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
7783 #[ inline]
7884 #[ unstable( feature = "lazy_cell" , issue = "109736" ) ]
7985 pub fn force ( this : & LazyCell < T , F > ) -> & T {
80- this. cell . get_or_init ( || match this. init . take ( ) {
81- Some ( f) => f ( ) ,
82- None => panic ! ( "`Lazy` instance has previously been poisoned" ) ,
83- } )
86+ let state = unsafe { & * this. state . get ( ) } ;
87+ match state {
88+ State :: Init ( data) => data,
89+ State :: Uninit ( _) => unsafe { LazyCell :: really_init ( this) } ,
90+ State :: Poisoned => panic ! ( "LazyCell has previously been poisoned" ) ,
91+ }
92+ }
93+
94+ /// # Safety
95+ /// May only be called when the state is `Uninit`.
96+ #[ cold]
97+ unsafe fn really_init ( this : & LazyCell < T , F > ) -> & T {
98+ let state = unsafe { & mut * this. state . get ( ) } ;
99+ // Temporarily mark the state as poisoned. This prevents reentrant
100+ // accesses and correctly poisons the cell if the closure panicked.
101+ let State :: Uninit ( f) = mem:: replace ( state, State :: Poisoned ) else { unreachable ! ( ) } ;
102+
103+ let data = f ( ) ;
104+
105+ // If the closure accessed the cell, the mutable borrow will be
106+ // invalidated, so create a new one here.
107+ let state = unsafe { & mut * this. state . get ( ) } ;
108+ * state = State :: Init ( data) ;
109+
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.
113+ let state = unsafe { & * this. state . get ( ) } ;
114+ let State :: Init ( data) = state else { unreachable ! ( ) } ;
115+ data
116+ }
117+ }
118+
119+ impl < T , F > LazyCell < T , F > {
120+ #[ inline]
121+ fn get ( & self ) -> Option < & T > {
122+ let state = unsafe { & * self . state . get ( ) } ;
123+ match state {
124+ State :: Init ( data) => Some ( data) ,
125+ _ => None ,
126+ }
84127 }
85128}
86129
@@ -105,6 +148,11 @@ impl<T: Default> Default for LazyCell<T> {
105148#[ unstable( feature = "lazy_cell" , issue = "109736" ) ]
106149impl < T : fmt:: Debug , F > fmt:: Debug for LazyCell < T , F > {
107150 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
108- f. debug_struct ( "Lazy" ) . field ( "cell" , & self . cell ) . field ( "init" , & ".." ) . finish ( )
151+ let mut d = f. debug_tuple ( "LazyCell" ) ;
152+ match self . get ( ) {
153+ Some ( data) => d. field ( data) ,
154+ None => d. field ( & format_args ! ( "<uninit>" ) ) ,
155+ } ;
156+ d. finish ( )
109157 }
110158}
0 commit comments