1
- use crate :: cell:: { Cell , OnceCell } ;
2
- use crate :: fmt;
3
1
use 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
+ }
4
11
5
12
/// A value which is initialized on the first access.
6
13
///
@@ -31,8 +38,7 @@ use crate::ops::Deref;
31
38
/// ```
32
39
#[ unstable( feature = "lazy_cell" , issue = "109736" ) ]
33
40
pub struct LazyCell < T , F = fn ( ) -> T > {
34
- cell : OnceCell < T > ,
35
- init : Cell < Option < F > > ,
41
+ state : UnsafeCell < State < T , F > > ,
36
42
}
37
43
38
44
impl < T , F : FnOnce ( ) -> T > LazyCell < T , F > {
@@ -53,8 +59,8 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
53
59
/// ```
54
60
#[ inline]
55
61
#[ 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 ) ) }
58
64
}
59
65
60
66
/// Forces the evaluation of this lazy value and returns a reference to
@@ -77,10 +83,47 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
77
83
#[ inline]
78
84
#[ unstable( feature = "lazy_cell" , issue = "109736" ) ]
79
85
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
+ }
84
127
}
85
128
}
86
129
@@ -105,6 +148,11 @@ impl<T: Default> Default for LazyCell<T> {
105
148
#[ unstable( feature = "lazy_cell" , issue = "109736" ) ]
106
149
impl < T : fmt:: Debug , F > fmt:: Debug for LazyCell < T , F > {
107
150
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 ( )
109
157
}
110
158
}
0 commit comments