6
6
# Summary
7
7
[ summary ] : #summary
8
8
9
- Deprecate ` mem::uninitialized::<T> ` and replace it with a ` MaybeUninit<T> ` type
10
- for safer and more principled handling of uninitialized data.
9
+ Deprecate ` mem::uninitialized::<T> ` and ` mem::zeroed::<T> ` and replace them with
10
+ a ` MaybeUninit<T> ` type for safer and more principled handling of uninitialized
11
+ data.
11
12
12
13
# Motivation
13
14
[ motivation ] : #motivation
14
15
15
16
The problems with ` uninitialized ` centre around its usage with uninhabited
16
- types. The concept of "uninitialized data" is extremely problematic when it
17
- comes into contact with types like ` ! ` or ` Void ` .
17
+ types, and its interaction with Rust's type layout invariants. The concept of
18
+ "uninitialized data" is extremely problematic when it comes into contact with
19
+ types like ` ! ` or ` Void ` .
18
20
19
21
For any given type, there may be valid and invalid bit-representations. For
20
22
example, the type ` u8 ` consists of a single byte and all possible bytes can be
@@ -53,6 +55,18 @@ fn mem::uninitialized::<!>() -> !
53
55
Yet calling this function does not diverge! It just breaks everything then eats
54
56
your laundry instead.
55
57
58
+ This problem is most prominent with ` ! ` but also applies to other types that
59
+ have restrictions on the values they can carry. For example,
60
+ ` Some(mem::uninitialized::<bool>()).is_none() ` could actually return ` true `
61
+ because uninitialized memory could violate the invariant that a ` bool ` is always
62
+ ` [00000000] ` or ` [00000001] ` -- and Rust relies on this invariant when doing
63
+ enum layout. So, ` mem::uninitialized::<bool>() ` is instantaneous undefined
64
+ behavior just like ` mem::uninitialized::<!>() ` . This also affects ` mem::zeroed `
65
+ when considering types where the all-` 0 ` bit pattern is not valid, like
66
+ references: ` mem::zeroed::<&'static i32>() ` is instantaneous undefined behavior.
67
+
68
+ ## Tracking uninitializedness in the type
69
+
56
70
An alternative way of representing uninitialized data is through a union type:
57
71
58
72
``` rust
@@ -63,14 +77,16 @@ union MaybeUninit<T> {
63
77
```
64
78
65
79
Instead of creating an "uninitialized value", we can create a ` MaybeUninit `
66
- initialized with ` uninit = () ` . Then, once we know that the value in the union
80
+ initialized with ` uninit: () ` . Then, once we know that the value in the union
67
81
is valid, we can extract it with ` my_uninit.value ` . This is a better way of
68
82
handling uninitialized data because it doesn't involve lying to the type system
69
83
and pretending that we have a value when we don't. It also better represents
70
84
what's actually going on: we never * really* have a value of type ` T ` when we're
71
85
using ` uninitialized::<T> ` , what we have is some memory that contains either a
72
86
value (` value: T ` ) or nothing (` uninit: () ` ), with it being the programmer's
73
- responsibility to keep track of which state we're in.
87
+ responsibility to keep track of which state we're in. Notice that creating a
88
+ ` MaybeUninit<T> ` is safe for any ` T ` ! Only when accessing ` my_uninit.value ` ,
89
+ we have to be careful to ensure this has been properly initialized.
74
90
75
91
To see how this can replace ` uninitialized ` and fix bugs in the process,
76
92
consider the following code:
@@ -143,72 +159,90 @@ library as a replacement.
143
159
Add the aforementioned ` MaybeUninit ` type to the standard library:
144
160
145
161
``` rust
146
- #[repr(transparent)]
147
- union MaybeUninit <T > {
162
+ pub union MaybeUninit <T > {
148
163
uninit : (),
149
- value : T ,
164
+ value : ManuallyDrop < T > ,
150
165
}
151
166
```
152
167
153
168
The type should have at least the following interface
169
+ ([ Playground link] ( https://play.rust-lang.org/?gist=81f5ab9a7e7107c9583de21382ef4333&version=nightly&mode=debug&edition=2015 ) ):
154
170
155
171
``` rust
156
172
impl <T > MaybeUninit <T > {
157
173
/// Create a new `MaybeUninit` in an uninitialized state.
174
+ ///
175
+ /// Note that dropping a `MaybeUninit` will never call `T`'s drop code.
176
+ /// It is your responsibility to make sure `T` gets dropped if it got initialized.
158
177
pub fn uninitialized () -> MaybeUninit <T > {
159
178
MaybeUninit {
160
179
uninit : (),
161
180
}
162
181
}
163
182
183
+ /// Create a new `MaybeUninit` in an uninitialized state, with the memory being
184
+ /// filled with `0` bytes. It depends on `T` whether that already makes for
185
+ /// proper initialization. For example, `MaybeUninit<usize>::zeroed()` is initialized,
186
+ /// but `MaybeUninit<&'static i32>::zeroed()` is not because references must not
187
+ /// be null.
188
+ ///
189
+ /// Note that dropping a `MaybeUninit` will never call `T`'s drop code.
190
+ /// It is your responsibility to make sure `T` gets dropped if it got initialized.
191
+ pub fn zeroed () -> MaybeUninit <T > {
192
+ let mut u = MaybeUninit :: <T >:: uninitialized ();
193
+ unsafe { u . as_mut_ptr (). write_bytes (0u8 , 1 ); }
194
+ u
195
+ }
196
+
164
197
/// Set the value of the `MaybeUninit`. The overwrites any previous value without dropping it.
165
- pub fn set (& mut self , val : T ) -> & mut T {
198
+ pub fn set (& mut self , val : T ) {
166
199
unsafe {
167
- self . value = val ;
168
- & mut self . value
200
+ self . value = ManuallyDrop :: new (val );
169
201
}
170
202
}
171
203
172
- /// Take the value of the `MaybeUninit`, putting it into an uninitialized state.
204
+ /// Extract the value from the `MaybeUninit` container. This is a great way
205
+ /// to ensure that the data will get dropped, because the resulting `T` is
206
+ /// subject to the usual drop handling.
173
207
///
174
208
/// # Unsafety
175
209
///
176
210
/// It is up to the caller to guarantee that the the `MaybeUninit` really is in an initialized
177
- /// state, otherwise undefined behaviour will result .
178
- pub unsafe fn get ( & self ) -> T {
179
- std :: ptr :: read (& self . value)
211
+ /// state, otherwise this will immediately cause undefined behavior .
212
+ pub unsafe fn into_inner ( self ) -> T {
213
+ std :: ptr :: read (& * self . value)
180
214
}
181
215
182
216
/// Get a reference to the contained value.
183
217
///
184
218
/// # Unsafety
185
219
///
186
220
/// It is up to the caller to guarantee that the the `MaybeUninit` really is in an initialized
187
- /// state, otherwise undefined behaviour will result .
221
+ /// state, otherwise this will immediately cause undefined behavior .
188
222
pub unsafe fn get_ref (& self ) -> & T {
189
- & self . value
223
+ & * self . value
190
224
}
191
225
192
226
/// Get a mutable reference to the contained value.
193
227
///
194
228
/// # Unsafety
195
229
///
196
230
/// It is up to the caller to guarantee that the the `MaybeUninit` really is in an initialized
197
- /// state, otherwise undefined behaviour will result .
231
+ /// state, otherwise this will immediately cause undefined behavior .
198
232
pub unsafe fn get_mut (& mut self ) -> & mut T {
199
- & mut self . value
233
+ & mut * self . value
200
234
}
201
235
202
- /// Get a pointer to the contained value. This pointer will only be valid if the `MaybeUninit`
203
- /// is in an initialized state .
236
+ /// Get a pointer to the contained value. Reading from this pointer will be undefined
237
+ /// behavior unless the `MaybeUninit` is initialized .
204
238
pub fn as_ptr (& self ) -> * const T {
205
- self as * const MaybeUninit < T > as * const T
239
+ unsafe { & * self . value as * const T }
206
240
}
207
241
208
- /// Get a mutable pointer to the contained value. This pointer will only be valid if the
209
- /// `MaybeUninit` is in an initialized state .
242
+ /// Get a mutable pointer to the contained value. Reading from this pointer will be undefined
243
+ /// behavior unless the `MaybeUninit` is initialized.
210
244
pub fn as_mut_ptr (& mut self ) -> * mut T {
211
- self as * mut MaybeUninit < T > as * mut T
245
+ unsafe { & mut * self . value as * mut T }
212
246
}
213
247
}
214
248
```
0 commit comments