ptr_ds/maybe_uninit.rs
1//! 未初期化の値。
2//!
3//! ## Preliminaries
4//!
5//! 実行時の所望のタイミングで初期化されるような値を作りたいことがある。
6//! 次のような Safe Rust のコードはコンパイルエラーとなる。
7//!
8//! ```compile_fail
9//! let a = 123;
10//! let mut b;
11//! if a % 5 <= 3 { b = "foo"; }
12//! if a % 7 <= 4 { b = "bar"; }
13//! let _invalid = b; // CE
14//! ```
15//!
16//! [`MaybeUninit`][`std::mem::MaybeUninit`] を使うことで、下記のように書くことができる。
17//!
18//! ```
19//! use std::mem::MaybeUninit;
20//!
21//! let a = 123;
22//! let mut b_uninit = MaybeUninit::<&str>::uninit();
23//! let ptr = b_uninit.as_mut_ptr();
24//! if a % 5 <= 3 { unsafe { ptr.write("foo") } };
25//! if a % 7 <= 4 { unsafe { ptr.write("bar") } };
26//! let b = unsafe { b_uninit.assume_init() };
27//! assert_eq!(b, "bar");
28//! ```
29//!
30//! `.assume_init()` を呼んだ時点で実際には未初期化だった場合、当然未定義動作となる。
31//!
32//! ```ignore
33//! use std::mem::MaybeUninit;
34//!
35//! let a = MaybeUninit::<i32>::uninit();
36//! let _invalid = unsafe { a.assume_init() };
37//! ```
38//!
39//! ```text
40//! --> src/lib.rs:6:25
41//! |
42//! 6 | let _invalid = unsafe { a.assume_init() };
43//! | ^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
44//! ```
45//!
46//! `MaybeUninit<T>` をメンバに持つ型 `U` に対する `MaybeUninit<U>` や、`[MaybeUninit<T>; N]`
47//! や、`Box<MaybeUninit<T>>` などの例を見てみる。
48//!
49//! ```
50//! use std::{mem::MaybeUninit, ptr};
51//!
52//! struct Foo {
53//! vector: Vec<i32>,
54//! string: MaybeUninit<String>,
55//! }
56//!
57//! let mut foo = unsafe {
58//! let mut foo_uninit = MaybeUninit::<Foo>::uninit();
59//! let ptr = foo_uninit.as_mut_ptr();
60//! // (*ptr).vector = vec![]; // UB
61//! ptr::addr_of_mut!((*ptr).vector).write(vec![0]);
62//! foo_uninit.assume_init()
63//! };
64//! assert_eq!(foo.vector, [0]);
65//!
66//! let s_ptr = foo.string.as_mut_ptr();
67//! unsafe {
68//! s_ptr.write("init".to_owned());
69//! assert_eq!(foo.string.assume_init_ref(), "init");
70//! drop(foo.string.assume_init_drop());
71//! }
72//! ```
73//!
74//! ```
75//! use std::mem::MaybeUninit;
76//!
77//! // let mut foo_uninit = [MaybeUninit::<String>::uninit(); 3]; // !Copy
78//! let mut foo_uninit = unsafe {
79//! MaybeUninit::<[MaybeUninit<String>; 3]>::uninit().assume_init()
80//! };
81//! unsafe {
82//! foo_uninit[0].write("uno".to_owned());
83//! foo_uninit[1].write("dos".to_owned());
84//!
85//! let foo = {
86//! &*(&foo_uninit[..2] as *const [MaybeUninit<_>] as *const [String])
87//! };
88//! assert_eq!(foo[0], "uno");
89//! assert_eq!(foo[1], "dos");
90//!
91//! foo_uninit[0].assume_init_drop();
92//! foo_uninit[1].assume_init_drop();
93//! }
94//! ```
95//!
96//! ```
97//! use std::{mem::MaybeUninit, ptr};
98//!
99//! struct Foo {
100//! vector: Vec<i32>,
101//! string: String,
102//! }
103//!
104//! let mut foo_uninit = Box::new(MaybeUninit::<Foo>::uninit());
105//! let foo: Box<Foo> = unsafe {
106//! let ptr = foo_uninit.as_mut_ptr();
107//! ptr::addr_of_mut!((*ptr).vector).write(vec![0]);
108//! ptr::addr_of_mut!((*ptr).string).write("init".to_owned());
109//! Box::from_raw(Box::leak(foo_uninit).as_mut_ptr())
110//! };
111//! assert_eq!(foo.vector, [0]);
112//! assert_eq!(foo.string, "init");
113//! ```
114//!
115//! レイアウトに関して、`T` と `MaybeUninit<T>` は同様の size および alignment を持つことは保証されている ([ref](https://doc.rust-lang.org/nightly/core/mem/union.MaybeUninit.html#layout-1))。
116//! `T` を含む型と `MaybeUninit<T>` を含む型が同様のレイアウトを持つとは限らないが、一旦は忘れておく。
117//!
118//! 未初期化な領域を含む状態で参照 `&mut T` を取得すると未定義動作となる。`ptr::addr_of_mut!`
119//! などを用いて回避する必要がある。
120//!
121//! ```
122//! use std::{mem::MaybeUninit, ptr};
123//!
124//! struct Foo {
125//! vector: Vec<i32>,
126//! string: String,
127//! }
128//!
129//! let mut foo = unsafe {
130//! let mut foo_uninit = MaybeUninit::<Foo>::uninit();
131//! let ptr = foo_uninit.as_mut_ptr();
132//! // (*ptr).vector = vec![0]; // UB
133//! ptr::addr_of_mut!((*ptr).vector).write(vec![0]);
134//! // (*ptr).string = "init".to_owned(); // UB
135//! ptr::addr_of_mut!((*ptr).string).write("init".to_owned());
136//! (*ptr).string = "re-init".to_owned(); // OK
137//!
138//! foo_uninit.assume_init()
139//! };
140//! ```
141//!
142//! 初期化済みの領域に `.write()` するとメモリリークが起きる。
143//! あるいは、未初期化の領域を `=` で書き込もうとすると、`*mut T` の dereference なり
144//! `&mut T` の取得なりをする必要があるため未定義動作となる。
145//!
146//! ```
147//! use std::mem::MaybeUninit;
148//!
149//! struct Foo(MaybeUninit<String>);
150//!
151//! let mut foo = Foo(MaybeUninit::uninit());
152//! // unsafe { *foo.0.as_mut_ptr() = "init".to_owned() } // UB
153//! // unsafe { *foo.0.assume_init_mut() = "init".to_owned() } // UB
154//! foo.0.write("init once".to_owned());
155//!
156//! unsafe { *foo.0.as_mut_ptr() = "init twice".to_owned() } // OK
157//! unsafe { *foo.0.assume_init_mut() = "init thrice".to_owned() } // OK
158//! // foo.0.write("init".to_owned()); // memory leak
159//!
160//! unsafe { foo.0.assume_init_drop() }
161//! ```
162//!
163//! `*mut ()` であれば、未初期化 (dangling) の領域を dereference しても大丈夫。
164//!
165//! ```
166//! use std::mem::MaybeUninit;
167//!
168//! let unit_uninit = MaybeUninit::<()>::uninit();
169//! unsafe {
170//! assert_eq!(unit_uninit.assume_init(), ());
171//! }
172//! ```
173
174#[cfg(test)]
175mod playground {
176 use std::{mem::MaybeUninit, ptr};
177
178 #[test]
179 fn nested() {
180 struct Foo {
181 i: i32,
182 s: String,
183 a: [MaybeUninit<i32>; 3],
184 }
185
186 let mut foo = unsafe {
187 let mut foo_uninit = MaybeUninit::<Foo>::uninit();
188 let ptr = foo_uninit.as_mut_ptr();
189 ptr::addr_of_mut!((*ptr).i).write(1);
190 // (*foo.as_mut_ptr()).s = "s".to_owned(); // UB
191 ptr::addr_of_mut!((*ptr).s).write("s".to_owned());
192 foo_uninit.assume_init()
193 };
194
195 foo.a[0].write(1);
196 assert_eq!(unsafe { foo.a[0].assume_init() }, 1);
197 }
198
199 #[test]
200 fn array() {
201 struct Foo<T, const N: usize> {
202 a: [MaybeUninit<T>; N],
203 }
204
205 let mut foo =
206 unsafe { MaybeUninit::<Foo<String, 3>>::uninit().assume_init() };
207
208 foo.a[0].write("one".to_owned());
209 // unsafe { *(foo.a[1].as_mut_ptr()) = "_".to_owned() }; // bad
210 // unsafe { *foo.a[1].assume_init_mut() = "_".to_owned() }; // also bad
211 foo.a[1].write("two".to_owned());
212
213 assert_eq!(unsafe { foo.a[0].assume_init_ref() }, "one");
214 assert_eq!(unsafe { foo.a[1].assume_init_ref() }, "two");
215
216 // foo.a[1].write("_".to_owned()); // bad
217 unsafe { *(foo.a[1].as_mut_ptr()) = "zwei".to_owned() }; // ok
218 unsafe { *foo.a[1].assume_init_mut() = "deux".to_owned() }; // also ok
219
220 let a = unsafe {
221 &mut *(&mut foo.a[..2] as *mut [MaybeUninit<_>] as *mut [String])
222 };
223 a[0] = "uno".to_owned();
224
225 let a = unsafe {
226 &*(&foo.a[..2] as *const [MaybeUninit<_>] as *const [String])
227 };
228 assert_eq!(a, ["uno", "deux"]);
229
230 unsafe { foo.a[0].assume_init_drop() };
231 unsafe { foo.a[1].assume_init_drop() };
232 }
233
234 #[test]
235 fn boxed() {
236 struct Foo {
237 a: String,
238 }
239
240 let mut foo_uninit = Box::new(MaybeUninit::<Foo>::uninit());
241 let foo = unsafe {
242 let ptr = foo_uninit.as_mut_ptr();
243 ptr::addr_of_mut!((*ptr).a).write("a".to_owned());
244 Box::from_raw(Box::leak(foo_uninit).as_mut_ptr())
245 };
246
247 assert_eq!(foo.a, "a");
248 }
249}