Skip to content

Commit a3b6e8e

Browse files
authored
Rollup merge of rust-lang#62451 - SimonSapin:new_uninit, r=RalfJung
Add APIs for uninitialized Box, Rc, and Arc. (Plus get_mut_unchecked) Assigning `MaybeUninit::<Foo>::uninit()` to a local variable is usually free, even when `size_of::<Foo>()` is large. However, passing it for example to `Arc::new` [causes at least one copy](https://youtu.be/F1AquroPfcI?t=4116) (from the stack to the newly allocated heap memory) even though there is no meaningful data. It is theoretically possible that a Sufficiently Advanced Compiler could optimize this copy away, but this is [reportedly unlikely to happen soon in LLVM](https://youtu.be/F1AquroPfcI?t=5431). This PR proposes two sets of features: * Constructors for containers (`Box`, `Rc`, `Arc`) of `MaybeUninit<T>` or `[MaybeUninit<T>]` that do not initialized the data, and unsafe conversions to the known-initialized types (without `MaybeUninit`). The constructors are guaranteed not to make unnecessary copies. * On `Rc` and `Arc`, an unsafe `get_mut_unchecked` method that provides `&mut T` access without checking the reference count. `Arc::get_mut` involves multiple atomic operations whose cost can be non-trivial. `Rc::get_mut` is less costly, but we add `Rc::get_mut_unchecked` anyway for symmetry with `Arc`. These can be useful independently, but they will presumably be typical when the new constructors of `Rc` and `Arc` are used. An alternative with a safe API would be to introduce `UniqueRc` and `UniqueArc` types that have the same memory layout as `Rc` and `Arc` (and so zero-cost conversion to them) but are guaranteed to have only one reference. But introducing entire new types feels “heavier” than new constructors on existing types, and initialization of `MaybeUninit<T>` typically requires unsafe code anyway. Summary of new APIs (all unstable in this PR): ```rust impl<T> Box<T> { pub fn new_uninit() -> Box<MaybeUninit<T>> {…} } impl<T> Box<MaybeUninit<T>> { pub unsafe fn assume_init(self) -> Box<T> {…} } impl<T> Box<[T]> { pub fn new_uninit_slice(len: usize) -> Box<[MaybeUninit<T>]> {…} } impl<T> Box<[MaybeUninit<T>]> { pub unsafe fn assume_init(self) -> Box<[T]> {…} } impl<T> Rc<T> { pub fn new_uninit() -> Rc<MaybeUninit<T>> {…} } impl<T> Rc<MaybeUninit<T>> { pub unsafe fn assume_init(self) -> Rc<T> {…} } impl<T> Rc<[T]> { pub fn new_uninit_slice(len: usize) -> Rc<[MaybeUninit<T>]> {…} } impl<T> Rc<[MaybeUninit<T>]> { pub unsafe fn assume_init(self) -> Rc<[T]> {…} } impl<T> Arc<T> { pub fn new_uninit() -> Arc<MaybeUninit<T>> {…} } impl<T> Arc<MaybeUninit<T>> { pub unsafe fn assume_init(self) -> Arc<T> {…} } impl<T> Arc<[T]> { pub fn new_uninit_slice(len: usize) -> Arc<[MaybeUninit<T>]> {…} } impl<T> Arc<[MaybeUninit<T>]> { pub unsafe fn assume_init(self) -> Arc<[T]> {…} } impl<T: ?Sized> Rc<T> { pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T {…} } impl<T: ?Sized> Arc<T> { pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T {…} } ```
2 parents d65e272 + 9bd7083 commit a3b6e8e

File tree

4 files changed

+505
-10
lines changed

4 files changed

+505
-10
lines changed

src/liballoc/boxed.rs

+135
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,10 @@ use core::ops::{
9191
CoerceUnsized, DispatchFromDyn, Deref, DerefMut, Receiver, Generator, GeneratorState
9292
};
9393
use core::ptr::{self, NonNull, Unique};
94+
use core::slice;
9495
use core::task::{Context, Poll};
9596

97+
use crate::alloc::{self, Global, Alloc};
9698
use crate::vec::Vec;
9799
use crate::raw_vec::RawVec;
98100
use crate::str::from_boxed_utf8_unchecked;
@@ -121,6 +123,34 @@ impl<T> Box<T> {
121123
box x
122124
}
123125

126+
/// Constructs a new box with uninitialized contents.
127+
///
128+
/// # Examples
129+
///
130+
/// ```
131+
/// #![feature(new_uninit)]
132+
///
133+
/// let mut five = Box::<u32>::new_uninit();
134+
///
135+
/// let five = unsafe {
136+
/// // Deferred initialization:
137+
/// five.as_mut_ptr().write(5);
138+
///
139+
/// five.assume_init()
140+
/// };
141+
///
142+
/// assert_eq!(*five, 5)
143+
/// ```
144+
#[unstable(feature = "new_uninit", issue = "63291")]
145+
pub fn new_uninit() -> Box<mem::MaybeUninit<T>> {
146+
let layout = alloc::Layout::new::<mem::MaybeUninit<T>>();
147+
let ptr = unsafe {
148+
Global.alloc(layout)
149+
.unwrap_or_else(|_| alloc::handle_alloc_error(layout))
150+
};
151+
Box(ptr.cast().into())
152+
}
153+
124154
/// Constructs a new `Pin<Box<T>>`. If `T` does not implement `Unpin`, then
125155
/// `x` will be pinned in memory and unable to be moved.
126156
#[stable(feature = "pin", since = "1.33.0")]
@@ -130,6 +160,111 @@ impl<T> Box<T> {
130160
}
131161
}
132162

163+
impl<T> Box<[T]> {
164+
/// Constructs a new boxed slice with uninitialized contents.
165+
///
166+
/// # Examples
167+
///
168+
/// ```
169+
/// #![feature(new_uninit)]
170+
///
171+
/// let mut values = Box::<[u32]>::new_uninit_slice(3);
172+
///
173+
/// let values = unsafe {
174+
/// // Deferred initialization:
175+
/// values[0].as_mut_ptr().write(1);
176+
/// values[1].as_mut_ptr().write(2);
177+
/// values[2].as_mut_ptr().write(3);
178+
///
179+
/// values.assume_init()
180+
/// };
181+
///
182+
/// assert_eq!(*values, [1, 2, 3])
183+
/// ```
184+
#[unstable(feature = "new_uninit", issue = "63291")]
185+
pub fn new_uninit_slice(len: usize) -> Box<[mem::MaybeUninit<T>]> {
186+
let layout = alloc::Layout::array::<mem::MaybeUninit<T>>(len).unwrap();
187+
let ptr = unsafe { alloc::alloc(layout) };
188+
let unique = Unique::new(ptr).unwrap_or_else(|| alloc::handle_alloc_error(layout));
189+
let slice = unsafe { slice::from_raw_parts_mut(unique.cast().as_ptr(), len) };
190+
Box(Unique::from(slice))
191+
}
192+
}
193+
194+
impl<T> Box<mem::MaybeUninit<T>> {
195+
/// Converts to `Box<T>`.
196+
///
197+
/// # Safety
198+
///
199+
/// As with [`MaybeUninit::assume_init`],
200+
/// it is up to the caller to guarantee that the value
201+
/// really is in an initialized state.
202+
/// Calling this when the content is not yet fully initialized
203+
/// causes immediate undefined behavior.
204+
///
205+
/// [`MaybeUninit::assume_init`]: ../../std/mem/union.MaybeUninit.html#method.assume_init
206+
///
207+
/// # Examples
208+
///
209+
/// ```
210+
/// #![feature(new_uninit)]
211+
///
212+
/// let mut five = Box::<u32>::new_uninit();
213+
///
214+
/// let five: Box<u32> = unsafe {
215+
/// // Deferred initialization:
216+
/// five.as_mut_ptr().write(5);
217+
///
218+
/// five.assume_init()
219+
/// };
220+
///
221+
/// assert_eq!(*five, 5)
222+
/// ```
223+
#[unstable(feature = "new_uninit", issue = "63291")]
224+
#[inline]
225+
pub unsafe fn assume_init(self) -> Box<T> {
226+
Box(Box::into_unique(self).cast())
227+
}
228+
}
229+
230+
impl<T> Box<[mem::MaybeUninit<T>]> {
231+
/// Converts to `Box<[T]>`.
232+
///
233+
/// # Safety
234+
///
235+
/// As with [`MaybeUninit::assume_init`],
236+
/// it is up to the caller to guarantee that the values
237+
/// really are in an initialized state.
238+
/// Calling this when the content is not yet fully initialized
239+
/// causes immediate undefined behavior.
240+
///
241+
/// [`MaybeUninit::assume_init`]: ../../std/mem/union.MaybeUninit.html#method.assume_init
242+
///
243+
/// # Examples
244+
///
245+
/// ```
246+
/// #![feature(new_uninit)]
247+
///
248+
/// let mut values = Box::<[u32]>::new_uninit_slice(3);
249+
///
250+
/// let values = unsafe {
251+
/// // Deferred initialization:
252+
/// values[0].as_mut_ptr().write(1);
253+
/// values[1].as_mut_ptr().write(2);
254+
/// values[2].as_mut_ptr().write(3);
255+
///
256+
/// values.assume_init()
257+
/// };
258+
///
259+
/// assert_eq!(*values, [1, 2, 3])
260+
/// ```
261+
#[unstable(feature = "new_uninit", issue = "63291")]
262+
#[inline]
263+
pub unsafe fn assume_init(self) -> Box<[T]> {
264+
Box(Unique::new_unchecked(Box::into_raw(self) as _))
265+
}
266+
}
267+
133268
impl<T: ?Sized> Box<T> {
134269
/// Constructs a box from a raw pointer.
135270
///

src/liballoc/rc.rs

+181-5
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,37 @@ impl<T> Rc<T> {
327327
}))
328328
}
329329

330+
/// Constructs a new `Rc` with uninitialized contents.
331+
///
332+
/// # Examples
333+
///
334+
/// ```
335+
/// #![feature(new_uninit)]
336+
/// #![feature(get_mut_unchecked)]
337+
///
338+
/// use std::rc::Rc;
339+
///
340+
/// let mut five = Rc::<u32>::new_uninit();
341+
///
342+
/// let five = unsafe {
343+
/// // Deferred initialization:
344+
/// Rc::get_mut_unchecked(&mut five).as_mut_ptr().write(5);
345+
///
346+
/// five.assume_init()
347+
/// };
348+
///
349+
/// assert_eq!(*five, 5)
350+
/// ```
351+
#[unstable(feature = "new_uninit", issue = "63291")]
352+
pub fn new_uninit() -> Rc<mem::MaybeUninit<T>> {
353+
unsafe {
354+
Rc::from_ptr(Rc::allocate_for_layout(
355+
Layout::new::<T>(),
356+
|mem| mem as *mut RcBox<mem::MaybeUninit<T>>,
357+
))
358+
}
359+
}
360+
330361
/// Constructs a new `Pin<Rc<T>>`. If `T` does not implement `Unpin`, then
331362
/// `value` will be pinned in memory and unable to be moved.
332363
#[stable(feature = "pin", since = "1.33.0")]
@@ -377,6 +408,118 @@ impl<T> Rc<T> {
377408
}
378409
}
379410

411+
impl<T> Rc<[T]> {
412+
/// Constructs a new reference-counted slice with uninitialized contents.
413+
///
414+
/// # Examples
415+
///
416+
/// ```
417+
/// #![feature(new_uninit)]
418+
/// #![feature(get_mut_unchecked)]
419+
///
420+
/// use std::rc::Rc;
421+
///
422+
/// let mut values = Rc::<[u32]>::new_uninit_slice(3);
423+
///
424+
/// let values = unsafe {
425+
/// // Deferred initialization:
426+
/// Rc::get_mut_unchecked(&mut values)[0].as_mut_ptr().write(1);
427+
/// Rc::get_mut_unchecked(&mut values)[1].as_mut_ptr().write(2);
428+
/// Rc::get_mut_unchecked(&mut values)[2].as_mut_ptr().write(3);
429+
///
430+
/// values.assume_init()
431+
/// };
432+
///
433+
/// assert_eq!(*values, [1, 2, 3])
434+
/// ```
435+
#[unstable(feature = "new_uninit", issue = "63291")]
436+
pub fn new_uninit_slice(len: usize) -> Rc<[mem::MaybeUninit<T>]> {
437+
unsafe {
438+
Rc::from_ptr(Rc::allocate_for_slice(len))
439+
}
440+
}
441+
}
442+
443+
impl<T> Rc<mem::MaybeUninit<T>> {
444+
/// Converts to `Rc<T>`.
445+
///
446+
/// # Safety
447+
///
448+
/// As with [`MaybeUninit::assume_init`],
449+
/// it is up to the caller to guarantee that the value
450+
/// really is in an initialized state.
451+
/// Calling this when the content is not yet fully initialized
452+
/// causes immediate undefined behavior.
453+
///
454+
/// [`MaybeUninit::assume_init`]: ../../std/mem/union.MaybeUninit.html#method.assume_init
455+
///
456+
/// # Examples
457+
///
458+
/// ```
459+
/// #![feature(new_uninit)]
460+
/// #![feature(get_mut_unchecked)]
461+
///
462+
/// use std::rc::Rc;
463+
///
464+
/// let mut five = Rc::<u32>::new_uninit();
465+
///
466+
/// let five = unsafe {
467+
/// // Deferred initialization:
468+
/// Rc::get_mut_unchecked(&mut five).as_mut_ptr().write(5);
469+
///
470+
/// five.assume_init()
471+
/// };
472+
///
473+
/// assert_eq!(*five, 5)
474+
/// ```
475+
#[unstable(feature = "new_uninit", issue = "63291")]
476+
#[inline]
477+
pub unsafe fn assume_init(self) -> Rc<T> {
478+
Rc::from_inner(mem::ManuallyDrop::new(self).ptr.cast())
479+
}
480+
}
481+
482+
impl<T> Rc<[mem::MaybeUninit<T>]> {
483+
/// Converts to `Rc<[T]>`.
484+
///
485+
/// # Safety
486+
///
487+
/// As with [`MaybeUninit::assume_init`],
488+
/// it is up to the caller to guarantee that the value
489+
/// really is in an initialized state.
490+
/// Calling this when the content is not yet fully initialized
491+
/// causes immediate undefined behavior.
492+
///
493+
/// [`MaybeUninit::assume_init`]: ../../std/mem/union.MaybeUninit.html#method.assume_init
494+
///
495+
/// # Examples
496+
///
497+
/// ```
498+
/// #![feature(new_uninit)]
499+
/// #![feature(get_mut_unchecked)]
500+
///
501+
/// use std::rc::Rc;
502+
///
503+
/// let mut values = Rc::<[u32]>::new_uninit_slice(3);
504+
///
505+
/// let values = unsafe {
506+
/// // Deferred initialization:
507+
/// Rc::get_mut_unchecked(&mut values)[0].as_mut_ptr().write(1);
508+
/// Rc::get_mut_unchecked(&mut values)[1].as_mut_ptr().write(2);
509+
/// Rc::get_mut_unchecked(&mut values)[2].as_mut_ptr().write(3);
510+
///
511+
/// values.assume_init()
512+
/// };
513+
///
514+
/// assert_eq!(*values, [1, 2, 3])
515+
/// ```
516+
#[unstable(feature = "new_uninit", issue = "63291")]
517+
#[inline]
518+
pub unsafe fn assume_init(self) -> Rc<[T]> {
519+
Rc::from_ptr(mem::ManuallyDrop::new(self).ptr.as_ptr() as _)
520+
}
521+
}
522+
380523
impl<T: ?Sized> Rc<T> {
381524
/// Consumes the `Rc`, returning the wrapped pointer.
382525
///
@@ -560,13 +703,46 @@ impl<T: ?Sized> Rc<T> {
560703
pub fn get_mut(this: &mut Self) -> Option<&mut T> {
561704
if Rc::is_unique(this) {
562705
unsafe {
563-
Some(&mut this.ptr.as_mut().value)
706+
Some(Rc::get_mut_unchecked(this))
564707
}
565708
} else {
566709
None
567710
}
568711
}
569712

713+
/// Returns a mutable reference to the inner value,
714+
/// without any check.
715+
///
716+
/// See also [`get_mut`], which is safe and does appropriate checks.
717+
///
718+
/// [`get_mut`]: struct.Rc.html#method.get_mut
719+
///
720+
/// # Safety
721+
///
722+
/// Any other `Rc` or [`Weak`] pointers to the same value must not be dereferenced
723+
/// for the duration of the returned borrow.
724+
/// This is trivially the case if no such pointers exist,
725+
/// for example immediately after `Rc::new`.
726+
///
727+
/// # Examples
728+
///
729+
/// ```
730+
/// #![feature(get_mut_unchecked)]
731+
///
732+
/// use std::rc::Rc;
733+
///
734+
/// let mut x = Rc::new(String::new());
735+
/// unsafe {
736+
/// Rc::get_mut_unchecked(&mut x).push_str("foo")
737+
/// }
738+
/// assert_eq!(*x, "foo");
739+
/// ```
740+
#[inline]
741+
#[unstable(feature = "get_mut_unchecked", issue = "63292")]
742+
pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T {
743+
&mut this.ptr.as_mut().value
744+
}
745+
570746
#[inline]
571747
#[stable(feature = "ptr_eq", since = "1.17.0")]
572748
/// Returns `true` if the two `Rc`s point to the same value (not
@@ -704,11 +880,11 @@ impl Rc<dyn Any> {
704880

705881
impl<T: ?Sized> Rc<T> {
706882
/// Allocates an `RcBox<T>` with sufficient space for
707-
/// an unsized value where the value has the layout provided.
883+
/// a possibly-unsized value where the value has the layout provided.
708884
///
709885
/// The function `mem_to_rcbox` is called with the data pointer
710886
/// and must return back a (potentially fat)-pointer for the `RcBox<T>`.
711-
unsafe fn allocate_for_unsized(
887+
unsafe fn allocate_for_layout(
712888
value_layout: Layout,
713889
mem_to_rcbox: impl FnOnce(*mut u8) -> *mut RcBox<T>
714890
) -> *mut RcBox<T> {
@@ -737,7 +913,7 @@ impl<T: ?Sized> Rc<T> {
737913
/// Allocates an `RcBox<T>` with sufficient space for an unsized value
738914
unsafe fn allocate_for_ptr(ptr: *const T) -> *mut RcBox<T> {
739915
// Allocate for the `RcBox<T>` using the given value.
740-
Self::allocate_for_unsized(
916+
Self::allocate_for_layout(
741917
Layout::for_value(&*ptr),
742918
|mem| set_data_ptr(ptr as *mut T, mem) as *mut RcBox<T>,
743919
)
@@ -768,7 +944,7 @@ impl<T: ?Sized> Rc<T> {
768944
impl<T> Rc<[T]> {
769945
/// Allocates an `RcBox<[T]>` with the given length.
770946
unsafe fn allocate_for_slice(len: usize) -> *mut RcBox<[T]> {
771-
Self::allocate_for_unsized(
947+
Self::allocate_for_layout(
772948
Layout::array::<T>(len).unwrap(),
773949
|mem| ptr::slice_from_raw_parts_mut(mem as *mut T, len) as *mut RcBox<[T]>,
774950
)

0 commit comments

Comments
 (0)