1
Fork 0

Rollup merge of #135394 - clarfonthey:uninit-slices-part-2, r=tgross35

`MaybeUninit` inherent slice methods part 2

These were moved out of #129259 since they require additional libs-api approval. Tracking issue: #117428.

New API surface:

```rust
impl<T> [MaybeUninit<T>] {
    // replacing fill; renamed to avoid conflict
    pub fn write_filled(&mut self, value: T) -> &mut [T] where T: Clone;

    // replacing fill_with; renamed to avoid conflict
    pub fn write_with<F>(&mut self, value: F) -> &mut [T] where F: FnMut() -> T;

    // renamed to remove "fill" terminology, since this is closer to the write_*_of_slice methods
    pub fn write_iter<I>(&mut self, iter: I) -> (&mut [T], &mut Self) where I: Iterator<Item = T>;
}
```

Relevant motivation for these methods; see #129259 for earlier methods' motiviations.

* I chose `write_filled` since `filled` is being used as an object here, whereas it's being used as an action in `fill`.
* I chose `write_with` instead of `write_filled_with` since it's shorter and still matches well.
* I chose `write_iter` because it feels completely different from the fill methods, and still has the intent clear.

In all of the methods, it felt appropriate to ensure that they contained `write` to clarify that they are effectively just special ways of doing `MaybeUninit::write` for each element of a slice.

Tracking issue: https://github.com/rust-lang/rust/issues/117428

r? libs-api
This commit is contained in:
Matthias Krüger 2025-03-19 16:52:52 +01:00 committed by GitHub
commit d46cc71f54
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 209 additions and 168 deletions

View file

@ -1065,161 +1065,46 @@ impl<T> MaybeUninit<T> {
this.write_clone_of_slice(src)
}
/// Fills a slice with elements by cloning `value`, returning a mutable reference to the now
/// initialized contents of the slice.
/// Any previously initialized elements will not be dropped.
///
/// This is similar to [`slice::fill`].
///
/// # Panics
///
/// This function will panic if any call to `Clone` panics.
///
/// If such a panic occurs, any elements previously initialized during this operation will be
/// dropped.
///
/// # Examples
///
/// ```
/// #![feature(maybe_uninit_fill)]
/// use std::mem::MaybeUninit;
///
/// let mut buf = [const { MaybeUninit::uninit() }; 10];
/// let initialized = MaybeUninit::fill(&mut buf, 1);
/// assert_eq!(initialized, &mut [1; 10]);
/// ```
#[doc(alias = "memset")]
/// Deprecated version of [`slice::write_filled`].
#[unstable(feature = "maybe_uninit_fill", issue = "117428")]
pub fn fill(this: &mut [MaybeUninit<T>], value: T) -> &mut [T]
#[deprecated(
note = "replaced by inherent write_filled method; will eventually be removed",
since = "1.83.0"
)]
pub fn fill<'a>(this: &'a mut [MaybeUninit<T>], value: T) -> &'a mut [T]
where
T: Clone,
{
SpecFill::spec_fill(this, value);
// SAFETY: Valid elements have just been filled into `this` so it is initialized
unsafe { this.assume_init_mut() }
this.write_filled(value)
}
/// Fills a slice with elements returned by calling a closure repeatedly.
///
/// This method uses a closure to create new values. If you'd rather `Clone` a given value, use
/// [`MaybeUninit::fill`]. If you want to use the `Default` trait to generate values, you can
/// pass [`Default::default`] as the argument.
///
/// # Panics
///
/// This function will panic if any call to the provided closure panics.
///
/// If such a panic occurs, any elements previously initialized during this operation will be
/// dropped.
///
/// # Examples
///
/// ```
/// #![feature(maybe_uninit_fill)]
/// use std::mem::MaybeUninit;
///
/// let mut buf = [const { MaybeUninit::<i32>::uninit() }; 10];
/// let initialized = MaybeUninit::fill_with(&mut buf, Default::default);
/// assert_eq!(initialized, &mut [0; 10]);
/// ```
/// Deprecated version of [`slice::write_with`].
#[unstable(feature = "maybe_uninit_fill", issue = "117428")]
pub fn fill_with<F>(this: &mut [MaybeUninit<T>], mut f: F) -> &mut [T]
#[deprecated(
note = "replaced by inherent write_with method; will eventually be removed",
since = "1.83.0"
)]
pub fn fill_with<'a, F>(this: &'a mut [MaybeUninit<T>], mut f: F) -> &'a mut [T]
where
F: FnMut() -> T,
{
let mut guard = Guard { slice: this, initialized: 0 };
for element in guard.slice.iter_mut() {
element.write(f());
guard.initialized += 1;
this.write_with(|_| f())
}
super::forget(guard);
// SAFETY: Valid elements have just been written into `this` so it is initialized
unsafe { this.assume_init_mut() }
}
/// Fills a slice with elements yielded by an iterator until either all elements have been
/// initialized or the iterator is empty.
///
/// Returns two slices. The first slice contains the initialized portion of the original slice.
/// The second slice is the still-uninitialized remainder of the original slice.
///
/// # Panics
///
/// This function panics if the iterator's `next` function panics.
///
/// If such a panic occurs, any elements previously initialized during this operation will be
/// dropped.
///
/// # Examples
///
/// Completely filling the slice:
///
/// ```
/// #![feature(maybe_uninit_fill)]
/// use std::mem::MaybeUninit;
///
/// let mut buf = [const { MaybeUninit::uninit() }; 5];
///
/// let iter = [1, 2, 3].into_iter().cycle();
/// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter);
///
/// assert_eq!(initialized, &mut [1, 2, 3, 1, 2]);
/// assert_eq!(remainder.len(), 0);
/// ```
///
/// Partially filling the slice:
///
/// ```
/// #![feature(maybe_uninit_fill)]
/// use std::mem::MaybeUninit;
///
/// let mut buf = [const { MaybeUninit::uninit() }; 5];
/// let iter = [1, 2];
/// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter);
///
/// assert_eq!(initialized, &mut [1, 2]);
/// assert_eq!(remainder.len(), 3);
/// ```
///
/// Checking an iterator after filling a slice:
///
/// ```
/// #![feature(maybe_uninit_fill)]
/// use std::mem::MaybeUninit;
///
/// let mut buf = [const { MaybeUninit::uninit() }; 3];
/// let mut iter = [1, 2, 3, 4, 5].into_iter();
/// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter.by_ref());
///
/// assert_eq!(initialized, &mut [1, 2, 3]);
/// assert_eq!(remainder.len(), 0);
/// assert_eq!(iter.as_slice(), &[4, 5]);
/// ```
/// Deprecated version of [`slice::write_iter`].
#[unstable(feature = "maybe_uninit_fill", issue = "117428")]
pub fn fill_from<I>(this: &mut [MaybeUninit<T>], it: I) -> (&mut [T], &mut [MaybeUninit<T>])
#[deprecated(
note = "replaced by inherent write_iter method; will eventually be removed",
since = "1.83.0"
)]
pub fn fill_from<'a, I>(
this: &'a mut [MaybeUninit<T>],
it: I,
) -> (&'a mut [T], &'a mut [MaybeUninit<T>])
where
I: IntoIterator<Item = T>,
{
let iter = it.into_iter();
let mut guard = Guard { slice: this, initialized: 0 };
for (element, val) in guard.slice.iter_mut().zip(iter) {
element.write(val);
guard.initialized += 1;
}
let initialized_len = guard.initialized;
super::forget(guard);
// SAFETY: guard.initialized <= this.len()
let (initted, remainder) = unsafe { this.split_at_mut_unchecked(initialized_len) };
// SAFETY: Valid elements have just been written into `init`, so that portion
// of `this` is initialized.
(unsafe { initted.assume_init_mut() }, remainder)
this.write_iter(it)
}
/// Deprecated version of [`slice::as_bytes`].
@ -1380,6 +1265,163 @@ impl<T> [MaybeUninit<T>] {
unsafe { self.assume_init_mut() }
}
/// Fills a slice with elements by cloning `value`, returning a mutable reference to the now
/// initialized contents of the slice.
/// Any previously initialized elements will not be dropped.
///
/// This is similar to [`slice::fill`].
///
/// # Panics
///
/// This function will panic if any call to `Clone` panics.
///
/// If such a panic occurs, any elements previously initialized during this operation will be
/// dropped.
///
/// # Examples
///
/// ```
/// #![feature(maybe_uninit_fill)]
/// use std::mem::MaybeUninit;
///
/// let mut buf = [const { MaybeUninit::uninit() }; 10];
/// let initialized = buf.write_filled(1);
/// assert_eq!(initialized, &mut [1; 10]);
/// ```
#[doc(alias = "memset")]
#[unstable(feature = "maybe_uninit_fill", issue = "117428")]
pub fn write_filled(&mut self, value: T) -> &mut [T]
where
T: Clone,
{
SpecFill::spec_fill(self, value);
// SAFETY: Valid elements have just been filled into `self` so it is initialized
unsafe { self.assume_init_mut() }
}
/// Fills a slice with elements returned by calling a closure for each index.
///
/// This method uses a closure to create new values. If you'd rather `Clone` a given value, use
/// [`MaybeUninit::fill`]. If you want to use the `Default` trait to generate values, you can
/// pass [`|_| Default::default()`][Default::default] as the argument.
///
/// # Panics
///
/// This function will panic if any call to the provided closure panics.
///
/// If such a panic occurs, any elements previously initialized during this operation will be
/// dropped.
///
/// # Examples
///
/// ```
/// #![feature(maybe_uninit_fill)]
/// use std::mem::MaybeUninit;
///
/// let mut buf = [const { MaybeUninit::<usize>::uninit() }; 5];
/// let initialized = buf.write_with(|idx| idx + 1);
/// assert_eq!(initialized, &mut [1, 2, 3, 4, 5]);
/// ```
#[unstable(feature = "maybe_uninit_fill", issue = "117428")]
pub fn write_with<F>(&mut self, mut f: F) -> &mut [T]
where
F: FnMut(usize) -> T,
{
let mut guard = Guard { slice: self, initialized: 0 };
for (idx, element) in guard.slice.iter_mut().enumerate() {
element.write(f(idx));
guard.initialized += 1;
}
super::forget(guard);
// SAFETY: Valid elements have just been written into `this` so it is initialized
unsafe { self.assume_init_mut() }
}
/// Fills a slice with elements yielded by an iterator until either all elements have been
/// initialized or the iterator is empty.
///
/// Returns two slices. The first slice contains the initialized portion of the original slice.
/// The second slice is the still-uninitialized remainder of the original slice.
///
/// # Panics
///
/// This function panics if the iterator's `next` function panics.
///
/// If such a panic occurs, any elements previously initialized during this operation will be
/// dropped.
///
/// # Examples
///
/// Completely filling the slice:
///
/// ```
/// #![feature(maybe_uninit_fill)]
/// use std::mem::MaybeUninit;
///
/// let mut buf = [const { MaybeUninit::uninit() }; 5];
///
/// let iter = [1, 2, 3].into_iter().cycle();
/// let (initialized, remainder) = buf.write_iter(iter);
///
/// assert_eq!(initialized, &mut [1, 2, 3, 1, 2]);
/// assert_eq!(remainder.len(), 0);
/// ```
///
/// Partially filling the slice:
///
/// ```
/// #![feature(maybe_uninit_fill)]
/// use std::mem::MaybeUninit;
///
/// let mut buf = [const { MaybeUninit::uninit() }; 5];
/// let iter = [1, 2];
/// let (initialized, remainder) = buf.write_iter(iter);
///
/// assert_eq!(initialized, &mut [1, 2]);
/// assert_eq!(remainder.len(), 3);
/// ```
///
/// Checking an iterator after filling a slice:
///
/// ```
/// #![feature(maybe_uninit_fill)]
/// use std::mem::MaybeUninit;
///
/// let mut buf = [const { MaybeUninit::uninit() }; 3];
/// let mut iter = [1, 2, 3, 4, 5].into_iter();
/// let (initialized, remainder) = buf.write_iter(iter.by_ref());
///
/// assert_eq!(initialized, &mut [1, 2, 3]);
/// assert_eq!(remainder.len(), 0);
/// assert_eq!(iter.as_slice(), &[4, 5]);
/// ```
#[unstable(feature = "maybe_uninit_fill", issue = "117428")]
pub fn write_iter<I>(&mut self, it: I) -> (&mut [T], &mut [MaybeUninit<T>])
where
I: IntoIterator<Item = T>,
{
let iter = it.into_iter();
let mut guard = Guard { slice: self, initialized: 0 };
for (element, val) in guard.slice.iter_mut().zip(iter) {
element.write(val);
guard.initialized += 1;
}
let initialized_len = guard.initialized;
super::forget(guard);
// SAFETY: guard.initialized <= self.len()
let (initted, remainder) = unsafe { self.split_at_mut_unchecked(initialized_len) };
// SAFETY: Valid elements have just been written into `init`, so that portion
// of `this` is initialized.
(unsafe { initted.assume_init_mut() }, remainder)
}
/// 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

View file

@ -1,5 +1,5 @@
use core::mem::*;
use core::ptr;
use core::{array, ptr};
#[cfg(panic = "unwind")]
use std::rc::Rc;
@ -327,11 +327,11 @@ fn uninit_write_clone_of_slice_no_drop() {
}
#[test]
fn uninit_fill() {
fn uninit_write_filled() {
let mut dst = [MaybeUninit::new(255); 64];
let expect = [0; 64];
assert_eq!(MaybeUninit::fill(&mut dst, 0), &expect);
assert_eq!(dst.write_filled(0), &expect);
}
#[cfg(panic = "unwind")]
@ -352,7 +352,7 @@ impl Clone for CloneUntilPanic {
#[test]
#[cfg(panic = "unwind")]
fn uninit_fill_clone_panic_drop() {
fn uninit_write_filled_panic_drop() {
use std::panic;
let rc = Rc::new(());
@ -361,7 +361,7 @@ fn uninit_fill_clone_panic_drop() {
let src = CloneUntilPanic { limit: 3, rc: rc.clone() };
let err = panic::catch_unwind(panic::AssertUnwindSafe(|| {
MaybeUninit::fill(&mut dst, src);
dst.write_filled(src);
}));
match err {
@ -378,23 +378,23 @@ fn uninit_fill_clone_panic_drop() {
#[test]
#[cfg(panic = "unwind")]
fn uninit_fill_clone_no_drop_clones() {
fn uninit_write_filled_no_drop_clones() {
let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()];
MaybeUninit::fill(&mut dst, Bomb);
dst.write_filled(Bomb);
}
#[test]
fn uninit_fill_with() {
let mut dst = [MaybeUninit::new(255); 64];
let expect = [0; 64];
fn uninit_write_with() {
let mut dst = [MaybeUninit::new(255usize); 64];
let expect = array::from_fn::<usize, 64, _>(|idx| idx);
assert_eq!(MaybeUninit::fill_with(&mut dst, || 0), &expect);
assert_eq!(dst.write_with(|idx| idx), &expect);
}
#[test]
#[cfg(panic = "unwind")]
fn uninit_fill_with_mid_panic() {
fn uninit_write_with_mid_panic() {
use std::panic;
let rc = Rc::new(());
@ -403,7 +403,7 @@ fn uninit_fill_with_mid_panic() {
let src = CloneUntilPanic { limit: 3, rc: rc.clone() };
let err = panic::catch_unwind(panic::AssertUnwindSafe(|| {
MaybeUninit::fill_with(&mut dst, || src.clone());
dst.write_with(|_| src.clone());
}));
drop(src);
@ -423,58 +423,58 @@ fn uninit_fill_with_mid_panic() {
#[test]
#[cfg(panic = "unwind")]
fn uninit_fill_with_no_drop() {
fn uninit_write_with_no_drop() {
let mut dst = [MaybeUninit::uninit()];
let src = Bomb;
MaybeUninit::fill_with(&mut dst, || src.clone());
dst.write_with(|_| src.clone());
forget(src);
}
#[test]
fn uninit_fill_from() {
fn uninit_write_iter() {
let mut dst = [MaybeUninit::new(255); 64];
let src = [0; 64];
let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter());
let (initted, remainder) = dst.write_iter(src.into_iter());
assert_eq!(initted, &src);
assert_eq!(remainder.len(), 0);
}
#[test]
fn uninit_fill_from_partial() {
fn uninit_write_iter_partial() {
let mut dst = [MaybeUninit::new(255); 64];
let src = [0; 48];
let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter());
let (initted, remainder) = dst.write_iter(src.into_iter());
assert_eq!(initted, &src);
assert_eq!(remainder.len(), 16);
}
#[test]
fn uninit_over_fill() {
fn uninit_write_iter_overfill() {
let mut dst = [MaybeUninit::new(255); 64];
let src = [0; 72];
let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter());
let (initted, remainder) = dst.write_iter(src.into_iter());
assert_eq!(initted, &src[0..64]);
assert_eq!(remainder.len(), 0);
}
#[test]
fn uninit_empty_fill() {
fn uninit_write_iter_empty() {
let mut dst = [MaybeUninit::new(255); 64];
let src = [0; 0];
let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter());
let (initted, remainder) = dst.write_iter(src.into_iter());
assert_eq!(initted, &src[0..0]);
assert_eq!(remainder.len(), 64);
}
#[test]
#[cfg(panic = "unwind")]
fn uninit_fill_from_mid_panic() {
fn uninit_write_iter_mid_panic() {
use std::panic;
struct IterUntilPanic {
@ -504,7 +504,7 @@ fn uninit_fill_from_mid_panic() {
let src = IterUntilPanic { limit: 3, rc: rc.clone() };
let err = panic::catch_unwind(panic::AssertUnwindSafe(|| {
MaybeUninit::fill_from(&mut dst, src);
dst.write_iter(src);
}));
match err {
@ -522,11 +522,11 @@ fn uninit_fill_from_mid_panic() {
#[test]
#[cfg(panic = "unwind")]
fn uninit_fill_from_no_drop() {
fn uninit_write_iter_no_drop() {
let mut dst = [MaybeUninit::uninit()];
let src = [Bomb];
MaybeUninit::fill_from(&mut dst, src.iter());
dst.write_iter(src.iter());
forget(src);
}

View file

@ -7,7 +7,6 @@ use crate::fmt;
use crate::io::{
self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write,
};
use crate::mem::MaybeUninit;
/// `Empty` ignores any data written via [`Write`], and will always be empty
/// (returning zero bytes) when read via [`Read`].
@ -196,7 +195,7 @@ impl Read for Repeat {
#[inline]
fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> {
// SAFETY: No uninit bytes are being written.
MaybeUninit::fill(unsafe { buf.as_mut() }, self.byte);
unsafe { buf.as_mut() }.write_filled(self.byte);
// SAFETY: the entire unfilled portion of buf has been initialized.
unsafe { buf.advance_unchecked(buf.capacity()) };
Ok(())