@@ -151,6 +151,43 @@ impl<T> Box<T> {
151
151
Box ( ptr. cast ( ) . into ( ) )
152
152
}
153
153
154
+ /// Allocates memory on the heap *then* constructs `T` in-place.
155
+ ///
156
+ /// If the allocation fails, the initialization closure won't be called; because the allocation
157
+ /// can fail, the optimizer often couldn't construct `T` in-place even in theory because having
158
+ /// the allocation happen first is an observable change if the code that creates the value has
159
+ /// side-effects such as being able to panic.
160
+ ///
161
+ /// FIXME: This is intended to work via return-value-optimization, but due to compiler
162
+ /// limitations can't reliably do that yet.
163
+ #[ inline( always) ]
164
+ fn new_in_place ( f : impl FnOnce ( ) -> T ) -> Box < T > {
165
+ let mut r: Box < mem:: MaybeUninit < T > > = Box :: new_uninit ( ) ;
166
+ let uninit: & mut mem:: MaybeUninit < T > = & mut * r;
167
+
168
+ unsafe {
169
+ // So why aren't we using ptr::write() here?
170
+ //
171
+ // For return-value-optimization to work, the compiler would have to call f with the
172
+ // pointer as the return value. But a pointer isn't guaranteed to actually point to
173
+ // valid memory: if the pointer was invalid, eg. due to being null, eliding the copy
174
+ // would change where the invalid memory access would occur, changing the behavior in
175
+ // an observable way.
176
+ //
177
+ // An invalid reference OTOH is undefined behavior, so the compiler is free to assume
178
+ // it is valid.
179
+ //
180
+ // Unfortunately, this optimization isn't actually implemented yet. Though if
181
+ // enough of f() can be inlined the compiler can generally elide the copy anyway.
182
+ //
183
+ // Finally, this is leak free because MaybeUninit::new() can't panic: either f()
184
+ // succesfully creates the value, and assume_init() is called, or f() panics, frees its
185
+ // resources, and r is deallocated as a Box<MaybeUninit<T>>
186
+ * uninit = mem:: MaybeUninit :: new ( f ( ) ) ;
187
+ r. assume_init ( )
188
+ }
189
+ }
190
+
154
191
/// Constructs a new `Pin<Box<T>>`. If `T` does not implement `Unpin`, then
155
192
/// `x` will be pinned in memory and unable to be moved.
156
193
#[ stable( feature = "pin" , since = "1.33.0" ) ]
@@ -480,7 +517,7 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Box<T> {
480
517
impl < T : Default > Default for Box < T > {
481
518
/// Creates a `Box<T>`, with the `Default` value for T.
482
519
fn default ( ) -> Box < T > {
483
- box Default :: default ( )
520
+ Box :: new_in_place ( T :: default )
484
521
}
485
522
}
486
523
0 commit comments