Skip to content

Commit

Permalink
Implement write() method for Box<MaybeUninit<T>>
Browse files Browse the repository at this point in the history
This adds method similar to `MaybeUninit::write` main difference being
it returns owned `Box`. This can be used to elide copy from stack
safely, however it's not currently tested that the optimization actually
occurs.

Analogous methods are not provided for `Rc` and `Arc` as those need to
handle the possibility of sharing. Some version of them may be added in
the future.

This was discussed in #63291 which this change extends.
  • Loading branch information
Kixunil committed Dec 2, 2021
1 parent 18bb8c6 commit 41e21aa
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 4 deletions.
6 changes: 2 additions & 4 deletions compiler/rustc_data_structures/src/functor.rs
Expand Up @@ -31,11 +31,9 @@ impl<T> IdFunctor for Box<T> {
let value = raw.read();
// SAFETY: Converts `Box<T>` to `Box<MaybeUninit<T>>` which is the
// inverse of `Box::assume_init()` and should be safe.
let mut raw: Box<mem::MaybeUninit<T>> = Box::from_raw(raw.cast());
let raw: Box<mem::MaybeUninit<T>> = Box::from_raw(raw.cast());
// SAFETY: Write the mapped value back into the `Box`.
raw.write(f(value)?);
// SAFETY: We just initialized `raw`.
raw.assume_init()
Box::write(raw, f(value)?)
})
}
}
Expand Down
36 changes: 36 additions & 0 deletions library/alloc/src/boxed.rs
Expand Up @@ -763,6 +763,42 @@ impl<T, A: Allocator> Box<mem::MaybeUninit<T>, A> {
let (raw, alloc) = Box::into_raw_with_allocator(self);
unsafe { Box::from_raw_in(raw as *mut T, alloc) }
}

/// Writes the value and converts to `Box<T, A>`.
///
/// This method converts the box similarly to [`Box::assume_init`] but
/// writes `value` into it before conversion thus guaranteeing safety.
/// In some scenarios use of this method may improve performance because
/// the compiler may be able to optimize copying from stack.
///
/// # Examples
///
/// ```
/// #![feature(new_uninit)]
///
/// let big_box = Box::<[usize; 1024]>::new_uninit();
///
/// let mut array = [0; 1024];
/// for (i, place) in array.iter_mut().enumerate() {
/// *place = i;
/// }
///
/// // The optimizer may be able to elide this copy, so previous code writes
/// // to heap directly.
/// let big_box = Box::write(big_box, array);
///
/// for (i, x) in big_box.iter().enumerate() {
/// assert_eq!(*x, i);
/// }
/// ```
#[unstable(feature = "new_uninit", issue = "63291")]
#[inline]
pub fn write(mut boxed: Self, value: T) -> Box<T, A> {
unsafe {
(*boxed).write(value);
boxed.assume_init()
}
}
}

impl<T, A: Allocator> Box<[mem::MaybeUninit<T>], A> {
Expand Down

0 comments on commit 41e21aa

Please sign in to comment.