Skip to content

Commit

Permalink
Tidy up maybe_uninit_write_slice API
Browse files Browse the repository at this point in the history
- Move methods to extensions

Signed-off-by: Alex Saveau <[email protected]>
  • Loading branch information
SUPERCILEX committed Oct 17, 2022
1 parent 8be3ce9 commit bb55063
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 161 deletions.
292 changes: 143 additions & 149 deletions library/core/src/mem/maybe_uninit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1014,154 +1014,6 @@ impl<T> MaybeUninit<T> {
this.as_mut_ptr() as *mut T
}

/// Copies the elements from `src` to `this`, returning a mutable reference to the now initialized contents of `this`.
///
/// If `T` does not implement `Copy`, use [`write_slice_cloned`]
///
/// This is similar to [`slice::copy_from_slice`].
///
/// # Panics
///
/// This function will panic if the two slices have different lengths.
///
/// # Examples
///
/// ```
/// #![feature(maybe_uninit_write_slice)]
/// use std::mem::MaybeUninit;
///
/// let mut dst = [MaybeUninit::uninit(); 32];
/// let src = [0; 32];
///
/// let init = MaybeUninit::write_slice(&mut dst, &src);
///
/// assert_eq!(init, src);
/// ```
///
/// ```
/// #![feature(maybe_uninit_write_slice)]
/// use std::mem::MaybeUninit;
///
/// let mut vec = Vec::with_capacity(32);
/// let src = [0; 16];
///
/// MaybeUninit::write_slice(&mut vec.spare_capacity_mut()[..src.len()], &src);
///
/// // SAFETY: we have just copied all the elements of len into the spare capacity
/// // the first src.len() elements of the vec are valid now.
/// unsafe {
/// vec.set_len(src.len());
/// }
///
/// assert_eq!(vec, src);
/// ```
///
/// [`write_slice_cloned`]: MaybeUninit::write_slice_cloned
#[unstable(feature = "maybe_uninit_write_slice", issue = "79995")]
pub fn write_slice<'a>(this: &'a mut [MaybeUninit<T>], src: &[T]) -> &'a mut [T]
where
T: Copy,
{
// SAFETY: &[T] and &[MaybeUninit<T>] have the same layout
let uninit_src: &[MaybeUninit<T>] = unsafe { super::transmute(src) };

this.copy_from_slice(uninit_src);

// SAFETY: Valid elements have just been copied into `this` so it is initialized
unsafe { MaybeUninit::slice_assume_init_mut(this) }
}

/// Clones the elements from `src` to `this`, returning a mutable reference to the now initialized contents of `this`.
/// Any already initialized elements will not be dropped.
///
/// If `T` implements `Copy`, use [`write_slice`]
///
/// This is similar to [`slice::clone_from_slice`] but does not drop existing elements.
///
/// # Panics
///
/// This function will panic if the two slices have different lengths, or if the implementation of `Clone` panics.
///
/// If there is a panic, the already cloned elements will be dropped.
///
/// # Examples
///
/// ```
/// #![feature(maybe_uninit_write_slice)]
/// use std::mem::MaybeUninit;
///
/// let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()];
/// let src = ["wibbly".to_string(), "wobbly".to_string(), "timey".to_string(), "wimey".to_string(), "stuff".to_string()];
///
/// let init = MaybeUninit::write_slice_cloned(&mut dst, &src);
///
/// assert_eq!(init, src);
/// ```
///
/// ```
/// #![feature(maybe_uninit_write_slice)]
/// use std::mem::MaybeUninit;
///
/// let mut vec = Vec::with_capacity(32);
/// let src = ["rust", "is", "a", "pretty", "cool", "language"];
///
/// MaybeUninit::write_slice_cloned(&mut vec.spare_capacity_mut()[..src.len()], &src);
///
/// // SAFETY: we have just cloned all the elements of len into the spare capacity
/// // the first src.len() elements of the vec are valid now.
/// unsafe {
/// vec.set_len(src.len());
/// }
///
/// assert_eq!(vec, src);
/// ```
///
/// [`write_slice`]: MaybeUninit::write_slice
#[unstable(feature = "maybe_uninit_write_slice", issue = "79995")]
pub fn write_slice_cloned<'a>(this: &'a mut [MaybeUninit<T>], src: &[T]) -> &'a mut [T]
where
T: Clone,
{
// unlike copy_from_slice this does not call clone_from_slice on the slice
// this is because `MaybeUninit<T: Clone>` does not implement Clone.

struct Guard<'a, T> {
slice: &'a mut [MaybeUninit<T>],
initialized: usize,
}

impl<'a, T> Drop for Guard<'a, T> {
fn drop(&mut self) {
let initialized_part = &mut self.slice[..self.initialized];
// SAFETY: this raw slice will contain only initialized objects
// that's why, it is allowed to drop it.
unsafe {
crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(initialized_part));
}
}
}

assert_eq!(this.len(), src.len(), "destination and source slices have different lengths");
// NOTE: We need to explicitly slice them to the same length
// for bounds checking to be elided, and the optimizer will
// generate memcpy for simple cases (for example T = u8).
let len = this.len();
let src = &src[..len];

// guard is needed b/c panic might happen during a clone
let mut guard = Guard { slice: this, initialized: 0 };

for i in 0..len {
guard.slice[i].write(src[i].clone());
guard.initialized += 1;
}

super::forget(guard);

// SAFETY: Valid elements have just been written into `this` so it is initialized
unsafe { MaybeUninit::slice_assume_init_mut(this) }
}

/// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes.
///
/// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still
Expand Down Expand Up @@ -1265,7 +1117,7 @@ impl<T> MaybeUninit<T> {
///
/// let mut uninit = [MaybeUninit::<u16>::uninit(), MaybeUninit::<u16>::uninit()];
/// let uninit_bytes = MaybeUninit::slice_as_bytes_mut(&mut uninit);
/// MaybeUninit::write_slice(uninit_bytes, &[0x12, 0x34, 0x56, 0x78]);
/// uninit_bytes.write_slice(&[0x12, 0x34, 0x56, 0x78]);
/// let vals = unsafe { MaybeUninit::slice_assume_init_ref(&uninit) };
/// if cfg!(target_endian = "little") {
/// assert_eq!(vals, &[0x3412u16, 0x7856u16]);
Expand Down Expand Up @@ -1321,3 +1173,145 @@ impl<T, const N: usize> [MaybeUninit<T>; N] {
unsafe { super::transmute_copy(&ManuallyDrop::new(self)) }
}
}

impl<T> [MaybeUninit<T>] {
/// Copies the elements from `src` to `this`, returning a mutable reference to the now initialized contents of `this`.
///
/// If `T` does not implement `Copy`, use [`write_slice_cloned`](slice::write_slice_cloned)
///
/// This is similar to [`slice::copy_from_slice`].
///
/// # Panics
///
/// This function will panic if the two slices have different lengths.
///
/// # Examples
///
/// ```
/// #![feature(maybe_uninit_write_slice)]
/// use std::mem::MaybeUninit;
///
/// let mut dst = [MaybeUninit::uninit(); 32];
/// let src = [0; 32];
///
/// let init = dst.write_slice(&src);
///
/// assert_eq!(init, src);
/// ```
///
/// ```
/// #![feature(maybe_uninit_write_slice)]
/// let mut vec = Vec::with_capacity(32);
/// let src = [0; 16];
///
/// vec.spare_capacity_mut()[..src.len()].write_slice(&src);
///
/// // SAFETY: we have just copied all the elements of len into the spare capacity
/// // the first src.len() elements of the vec are valid now.
/// unsafe {
/// vec.set_len(src.len());
/// }
///
/// assert_eq!(vec, src);
/// ```
#[unstable(feature = "maybe_uninit_write_slice", issue = "79995")]
pub fn write_slice(&mut self, src: &[T]) -> &mut [T]
where
T: Copy,
{
// SAFETY: &[T] and &[MaybeUninit<T>] have the same layout
let uninit_src: &[MaybeUninit<T>] = unsafe { super::transmute(src) };

self.copy_from_slice(uninit_src);

// SAFETY: Valid elements have just been copied into `this` so it is initialized
unsafe { MaybeUninit::slice_assume_init_mut(self) }
}

/// Clones the elements from `src` to `this`, returning a mutable reference to the now initialized contents of `this`.
/// Any already initialized elements will not be dropped.
///
/// If `T` implements `Copy`, use [`write_slice`](slice::write_slice)
///
/// This is similar to [`slice::clone_from_slice`] but does not drop existing elements.
///
/// # Panics
///
/// This function will panic if the two slices have different lengths, or if the implementation of `Clone` panics.
///
/// If there is a panic, the already cloned elements will be dropped.
///
/// # Examples
///
/// ```
/// #![feature(maybe_uninit_write_slice)]
/// use std::mem::MaybeUninit;
///
/// let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()];
/// let src = ["wibbly".to_string(), "wobbly".to_string(), "timey".to_string(), "wimey".to_string(), "stuff".to_string()];
///
/// let init = dst.write_slice_cloned(&src);
///
/// assert_eq!(init, src);
/// ```
///
/// ```
/// #![feature(maybe_uninit_write_slice)]
/// let mut vec = Vec::with_capacity(32);
/// let src = ["rust", "is", "a", "pretty", "cool", "language"];
///
/// vec.spare_capacity_mut()[..src.len()].write_slice_cloned(&src);
///
/// // SAFETY: we have just cloned all the elements of len into the spare capacity
/// // the first src.len() elements of the vec are valid now.
/// unsafe {
/// vec.set_len(src.len());
/// }
///
/// assert_eq!(vec, src);
/// ```
#[unstable(feature = "maybe_uninit_write_slice", issue = "79995")]
pub fn write_slice_cloned(&mut self, src: &[T]) -> &mut [T]
where
T: Clone,
{
// unlike copy_from_slice this does not call clone_from_slice on the slice
// this is because `MaybeUninit<T: Clone>` does not implement Clone.

struct Guard<'a, T> {
slice: &'a mut [MaybeUninit<T>],
initialized: usize,
}

impl<'a, T> Drop for Guard<'a, T> {
fn drop(&mut self) {
let initialized_part = &mut self.slice[..self.initialized];
// SAFETY: this raw slice will contain only initialized objects
// that's why, it is allowed to drop it.
unsafe {
ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(initialized_part));
}
}
}

assert_eq!(self.len(), src.len(), "destination and source slices have different lengths");
// NOTE: We need to explicitly slice them to the same length
// for bounds checking to be elided, and the optimizer will
// generate memcpy for simple cases (for example T = u8).
let len = self.len();
let src = &src[..len];

// guard is needed b/c panic might happen during a clone
let mut guard = Guard { slice: self, initialized: 0 };

for i in 0..len {
guard.slice[i].write(src[i].clone());
guard.initialized += 1;
}

super::forget(guard);

// SAFETY: Valid elements have just been written into `self` so it is initialized
unsafe { MaybeUninit::slice_assume_init_mut(self) }
}
}
18 changes: 9 additions & 9 deletions library/core/tests/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ fn uninit_write_slice() {
let mut dst = [MaybeUninit::new(255); 64];
let src = [0; 64];

assert_eq!(MaybeUninit::write_slice(&mut dst, &src), &src);
assert_eq!(dst.write_slice(&src), &src);
}

#[test]
Expand All @@ -191,7 +191,7 @@ fn uninit_write_slice_panic_lt() {
let mut dst = [MaybeUninit::uninit(); 64];
let src = [0; 32];

MaybeUninit::write_slice(&mut dst, &src);
dst.write_slice(&src);
}

#[test]
Expand All @@ -200,15 +200,15 @@ fn uninit_write_slice_panic_gt() {
let mut dst = [MaybeUninit::uninit(); 64];
let src = [0; 128];

MaybeUninit::write_slice(&mut dst, &src);
dst.write_slice(&src);
}

#[test]
fn uninit_clone_from_slice() {
fn uninit_write_slice_cloned() {
let mut dst = [MaybeUninit::new(255); 64];
let src = [0; 64];

assert_eq!(MaybeUninit::write_slice_cloned(&mut dst, &src), &src);
assert_eq!(dst.write_slice_cloned(&src), &src);
}

#[test]
Expand All @@ -217,7 +217,7 @@ fn uninit_write_slice_cloned_panic_lt() {
let mut dst = [MaybeUninit::uninit(); 64];
let src = [0; 32];

MaybeUninit::write_slice_cloned(&mut dst, &src);
dst.write_slice_cloned(&src);
}

#[test]
Expand All @@ -226,7 +226,7 @@ fn uninit_write_slice_cloned_panic_gt() {
let mut dst = [MaybeUninit::uninit(); 64];
let src = [0; 128];

MaybeUninit::write_slice_cloned(&mut dst, &src);
dst.write_slice_cloned(&src);
}

#[test]
Expand Down Expand Up @@ -267,7 +267,7 @@ fn uninit_write_slice_cloned_mid_panic() {
];

let err = panic::catch_unwind(panic::AssertUnwindSafe(|| {
MaybeUninit::write_slice_cloned(&mut dst, &src);
dst.write_slice_cloned(&src);
}));

drop(src);
Expand Down Expand Up @@ -299,7 +299,7 @@ fn uninit_write_slice_cloned_no_drop() {
let mut dst = [MaybeUninit::uninit()];
let src = [Bomb];

MaybeUninit::write_slice_cloned(&mut dst, &src);
dst.write_slice_cloned(&src);

forget(src);
}
Expand Down
2 changes: 1 addition & 1 deletion library/proc_macro/src/bridge/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ impl Arena {

pub(crate) fn alloc_str<'a>(&'a self, string: &str) -> &'a mut str {
let alloc = self.alloc_raw(string.len());
let bytes = MaybeUninit::write_slice(alloc, string.as_bytes());
let bytes = alloc.write_slice(string.as_bytes());

// SAFETY: we convert from `&str` to `&[u8]`, clone it into the arena,
// and immediately convert the clone back to `&str`.
Expand Down
Loading

0 comments on commit bb55063

Please sign in to comment.