Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
4479393
array_chunks: slightly improve docs
hkBst Oct 29, 2025
f49376f
slice iter: replace manual saturating_sub
hkBst Sep 10, 2025
2d0a011
slice iter: cleanup and make similar Chunks[Mut]::nth
hkBst Sep 11, 2025
6a0ad41
slice iter: more cleanup
hkBst Sep 11, 2025
51b3404
slice iter: rm all overflowing_* and switch conditions to mv dependen…
hkBst Nov 4, 2025
e9c1235
add a coretest checking `TryInto`/`TryFrom` impls
WaffleLapkin Dec 1, 2025
33bb39c
also introduce Peekable::next_if_map_mut next to next_if_map
jdonszelmann Dec 1, 2025
e1d259b
This statement is misleading
spastorino Nov 23, 2025
6fddb8f
Remove initialized-bytes tracking from `BorrowedBuf` and `BorrowedCur…
joshtriplett Nov 14, 2025
94a0f12
Update other targets for `BorrowedCursor` unstable API changes
joshtriplett Dec 2, 2025
e5f814a
std: sys: fs: uefi: Make time in FileAttr optional
Ayush1325 Dec 2, 2025
3f2683b
Rollup merge of #146436 - hkBst:slice-iter-1, r=joboet
matthiaskrgr Dec 2, 2025
56b9c79
Rollup merge of #148250 - hkBst:array-chunks-docs, r=joboet
matthiaskrgr Dec 2, 2025
ab2df9c
Rollup merge of #149520 - jdonszelmann:new-map-if, r=joboet
matthiaskrgr Dec 2, 2025
20bc9be
Rollup merge of #149538 - Ayush1325:uefi-fs-time-fix, r=joboet
matthiaskrgr Dec 2, 2025
9246180
Rollup merge of #148937 - joshtriplett:borrowed-buf-no-init-tracking,…
matthiaskrgr Dec 3, 2025
bccd1da
Rollup merge of #148918 - WaffleLapkin:tryfromwhattttt, r=jdonszelmann
matthiaskrgr Dec 3, 2025
2914d59
Rollup merge of #149244 - spastorino:fix-mem-drop-rustdoc, r=ChrisDenton
matthiaskrgr Dec 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions core/src/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -924,7 +924,7 @@ const fn try_from_fn_erased<R: [const] Try<Output: [const] Destruct>>(
/// All write accesses to this structure are unsafe and must maintain a correct
/// count of `initialized` elements.
///
/// To minimize indirection fields are still pub but callers should at least use
/// To minimize indirection, fields are still pub but callers should at least use
/// `push_unchecked` to signal that something unsafe is going on.
struct Guard<'a, T> {
/// The array to be initialized.
Expand All @@ -943,7 +943,7 @@ impl<T> Guard<'_, T> {
#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")]
pub(crate) const unsafe fn push_unchecked(&mut self, item: T) {
// SAFETY: If `initialized` was correct before and the caller does not
// invoke this method more than N times then writes will be in-bounds
// invoke this method more than N times, then writes will be in-bounds
// and slots will not be initialized more than once.
unsafe {
self.array_mut.get_unchecked_mut(self.initialized).write(item);
Expand Down Expand Up @@ -972,7 +972,7 @@ impl<T: [const] Destruct> const Drop for Guard<'_, T> {
/// `next` at most `N` times, the iterator can still be used afterwards to
/// retrieve the remaining items.
///
/// If `iter.next()` panicks, all items already yielded by the iterator are
/// If `iter.next()` panics, all items already yielded by the iterator are
/// dropped.
///
/// Used for [`Iterator::next_chunk`].
Expand Down Expand Up @@ -1004,6 +1004,7 @@ fn iter_next_chunk_erased<T>(
buffer: &mut [MaybeUninit<T>],
iter: &mut impl Iterator<Item = T>,
) -> Result<(), usize> {
// if `Iterator::next` panics, this guard will drop already initialized items
let mut guard = Guard { array_mut: buffer, initialized: 0 };
while guard.initialized < guard.array_mut.len() {
let Some(item) = iter.next() else {
Expand Down
162 changes: 28 additions & 134 deletions core/src/io/borrowed_buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,38 @@

use crate::fmt::{self, Debug, Formatter};
use crate::mem::{self, MaybeUninit};
use crate::{cmp, ptr};

/// A borrowed byte buffer which is incrementally filled and initialized.
/// A borrowed buffer of initially uninitialized bytes, which is incrementally filled.
///
/// This type is a sort of "double cursor". It tracks three regions in the buffer: a region at the beginning of the
/// buffer that has been logically filled with data, a region that has been initialized at some point but not yet
/// logically filled, and a region at the end that is fully uninitialized. The filled region is guaranteed to be a
/// subset of the initialized region.
/// This type makes it safer to work with `MaybeUninit` buffers, such as to read into a buffer
/// without having to initialize it first. It tracks the region of bytes that have been filled and
/// the region that remains uninitialized.
///
/// In summary, the contents of the buffer can be visualized as:
/// The contents of the buffer can be visualized as:
/// ```not_rust
/// [ capacity ]
/// [ filled | unfilled ]
/// [ initialized | uninitialized ]
/// [ capacity ]
/// [ len: filled and initialized | capacity - len: uninitialized ]
/// ```
///
/// A `BorrowedBuf` is created around some existing data (or capacity for data) via a unique reference
/// (`&mut`). The `BorrowedBuf` can be configured (e.g., using `clear` or `set_init`), but cannot be
/// directly written. To write into the buffer, use `unfilled` to create a `BorrowedCursor`. The cursor
/// has write-only access to the unfilled portion of the buffer (you can think of it as a
/// write-only iterator).
/// Note that `BorrowedBuf` does not distinguish between uninitialized data and data that was
/// previously initialized but no longer contains valid data.
///
/// A `BorrowedBuf` is created around some existing data (or capacity for data) via a unique
/// reference (`&mut`). The `BorrowedBuf` can be configured (e.g., using `clear` or `set_len`), but
/// cannot be directly written. To write into the buffer, use `unfilled` to create a
/// `BorrowedCursor`. The cursor has write-only access to the unfilled portion of the buffer.
///
/// The lifetime `'data` is a bound on the lifetime of the underlying data.
pub struct BorrowedBuf<'data> {
/// The buffer's underlying data.
buf: &'data mut [MaybeUninit<u8>],
/// The length of `self.buf` which is known to be filled.
filled: usize,
/// The length of `self.buf` which is known to be initialized.
init: usize,
}

impl Debug for BorrowedBuf<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("BorrowedBuf")
.field("init", &self.init)
.field("filled", &self.filled)
.field("capacity", &self.capacity())
.finish()
Expand All @@ -48,24 +44,22 @@ impl Debug for BorrowedBuf<'_> {
impl<'data> From<&'data mut [u8]> for BorrowedBuf<'data> {
#[inline]
fn from(slice: &'data mut [u8]) -> BorrowedBuf<'data> {
let len = slice.len();

BorrowedBuf {
// SAFETY: initialized data never becoming uninitialized is an invariant of BorrowedBuf
// SAFETY: Always in bounds. We treat the buffer as uninitialized, even though it's
// already initialized.
buf: unsafe { (slice as *mut [u8]).as_uninit_slice_mut().unwrap() },
filled: 0,
init: len,
}
}
}

/// Creates a new `BorrowedBuf` from an uninitialized buffer.
///
/// Use `set_init` if part of the buffer is known to be already initialized.
/// Use `set_filled` if part of the buffer is known to be already filled.
impl<'data> From<&'data mut [MaybeUninit<u8>]> for BorrowedBuf<'data> {
#[inline]
fn from(buf: &'data mut [MaybeUninit<u8>]) -> BorrowedBuf<'data> {
BorrowedBuf { buf, filled: 0, init: 0 }
BorrowedBuf { buf, filled: 0 }
}
}

Expand All @@ -74,14 +68,11 @@ impl<'data> From<&'data mut [MaybeUninit<u8>]> for BorrowedBuf<'data> {
/// Use `BorrowedCursor::with_unfilled_buf` instead for a safer alternative.
impl<'data> From<BorrowedCursor<'data>> for BorrowedBuf<'data> {
#[inline]
fn from(mut buf: BorrowedCursor<'data>) -> BorrowedBuf<'data> {
let init = buf.init_mut().len();
fn from(buf: BorrowedCursor<'data>) -> BorrowedBuf<'data> {
BorrowedBuf {
// SAFETY: no initialized byte is ever uninitialized as per
// `BorrowedBuf`'s invariant
// SAFETY: Always in bounds. We treat the buffer as uninitialized.
buf: unsafe { buf.buf.buf.get_unchecked_mut(buf.buf.filled..) },
filled: 0,
init,
}
}
}
Expand All @@ -99,12 +90,6 @@ impl<'data> BorrowedBuf<'data> {
self.filled
}

/// Returns the length of the initialized part of the buffer.
#[inline]
pub fn init_len(&self) -> usize {
self.init
}

/// Returns a shared reference to the filled portion of the buffer.
#[inline]
pub fn filled(&self) -> &[u8] {
Expand Down Expand Up @@ -159,33 +144,16 @@ impl<'data> BorrowedBuf<'data> {

/// Clears the buffer, resetting the filled region to empty.
///
/// The number of initialized bytes is not changed, and the contents of the buffer are not modified.
/// The contents of the buffer are not modified.
#[inline]
pub fn clear(&mut self) -> &mut Self {
self.filled = 0;
self
}

/// Asserts that the first `n` bytes of the buffer are initialized.
///
/// `BorrowedBuf` assumes that bytes are never de-initialized, so this method does nothing when called with fewer
/// bytes than are already known to be initialized.
///
/// # Safety
///
/// The caller must ensure that the first `n` unfilled bytes of the buffer have already been initialized.
#[inline]
pub unsafe fn set_init(&mut self, n: usize) -> &mut Self {
self.init = cmp::max(self.init, n);
self
}
}

/// A writeable view of the unfilled portion of a [`BorrowedBuf`].
///
/// The unfilled portion consists of an initialized and an uninitialized part; see [`BorrowedBuf`]
/// for details.
///
/// Data can be written directly to the cursor by using [`append`](BorrowedCursor::append) or
/// indirectly by getting a slice of part or all of the cursor and writing into the slice. In the
/// indirect case, the caller must call [`advance`](BorrowedCursor::advance) after writing to inform
Expand Down Expand Up @@ -238,48 +206,17 @@ impl<'a> BorrowedCursor<'a> {
self.buf.filled
}

/// Returns a mutable reference to the initialized portion of the cursor.
#[inline]
pub fn init_mut(&mut self) -> &mut [u8] {
// SAFETY: We only slice the initialized part of the buffer, which is always valid
unsafe {
let buf = self.buf.buf.get_unchecked_mut(self.buf.filled..self.buf.init);
buf.assume_init_mut()
}
}

/// Returns a mutable reference to the whole cursor.
///
/// # Safety
///
/// The caller must not uninitialize any bytes in the initialized portion of the cursor.
/// The caller must not uninitialize any previously initialized bytes.
#[inline]
pub unsafe fn as_mut(&mut self) -> &mut [MaybeUninit<u8>] {
// SAFETY: always in bounds
unsafe { self.buf.buf.get_unchecked_mut(self.buf.filled..) }
}

/// Advances the cursor by asserting that `n` bytes have been filled.
///
/// After advancing, the `n` bytes are no longer accessible via the cursor and can only be
/// accessed via the underlying buffer. I.e., the buffer's filled portion grows by `n` elements
/// and its unfilled portion (and the capacity of this cursor) shrinks by `n` elements.
///
/// If less than `n` bytes initialized (by the cursor's point of view), `set_init` should be
/// called first.
///
/// # Panics
///
/// Panics if there are less than `n` bytes initialized.
#[inline]
pub fn advance(&mut self, n: usize) -> &mut Self {
// The subtraction cannot underflow by invariant of this type.
assert!(n <= self.buf.init - self.buf.filled);

self.buf.filled += n;
self
}

/// Advances the cursor by asserting that `n` bytes have been filled.
///
/// After advancing, the `n` bytes are no longer accessible via the cursor and can only be
Expand All @@ -288,42 +225,11 @@ impl<'a> BorrowedCursor<'a> {
///
/// # Safety
///
/// The caller must ensure that the first `n` bytes of the cursor have been properly
/// initialised.
/// The caller must ensure that the first `n` bytes of the cursor have been initialized. `n`
/// must not exceed the remaining capacity of this cursor.
#[inline]
pub unsafe fn advance_unchecked(&mut self, n: usize) -> &mut Self {
pub unsafe fn advance(&mut self, n: usize) -> &mut Self {
self.buf.filled += n;
self.buf.init = cmp::max(self.buf.init, self.buf.filled);
self
}

/// Initializes all bytes in the cursor.
#[inline]
pub fn ensure_init(&mut self) -> &mut Self {
// SAFETY: always in bounds and we never uninitialize these bytes.
let uninit = unsafe { self.buf.buf.get_unchecked_mut(self.buf.init..) };

// SAFETY: 0 is a valid value for MaybeUninit<u8> and the length matches the allocation
// since it is comes from a slice reference.
unsafe {
ptr::write_bytes(uninit.as_mut_ptr(), 0, uninit.len());
}
self.buf.init = self.buf.capacity();

self
}

/// Asserts that the first `n` unfilled bytes of the cursor are initialized.
///
/// `BorrowedBuf` assumes that bytes are never de-initialized, so this method does nothing when
/// called with fewer bytes than are already known to be initialized.
///
/// # Safety
///
/// The caller must ensure that the first `n` bytes of the buffer have already been initialized.
#[inline]
pub unsafe fn set_init(&mut self, n: usize) -> &mut Self {
self.buf.init = cmp::max(self.buf.init, self.buf.filled + n);
self
}

Expand All @@ -341,10 +247,6 @@ impl<'a> BorrowedCursor<'a> {
self.as_mut()[..buf.len()].write_copy_of_slice(buf);
}

// SAFETY: We just added the entire contents of buf to the filled section.
unsafe {
self.set_init(buf.len());
}
self.buf.filled += buf.len();
}

Expand All @@ -367,17 +269,9 @@ impl<'a> BorrowedCursor<'a> {
// there, one could mark some bytes as initialized even though there aren't.
assert!(core::ptr::addr_eq(prev_ptr, buf.buf));

let filled = buf.filled;
let init = buf.init;

// Update `init` and `filled` fields with what was written to the buffer.
// `self.buf.filled` was the starting length of the `BorrowedBuf`.
//
// SAFETY: These amounts of bytes were initialized/filled in the `BorrowedBuf`,
// and therefore they are initialized/filled in the cursor too, because the
// buffer wasn't replaced.
self.buf.init = self.buf.filled + init;
self.buf.filled += filled;
// SAFETY: These bytes were filled in the `BorrowedBuf`, so they're filled in the cursor
// too, because the buffer wasn't replaced.
self.buf.filled += buf.filled;

res
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/iter/adapters/array_chunks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ where
match self.iter.next_chunk() {
Ok(chunk) => acc = f(acc, chunk)?,
Err(remainder) => {
// Make sure to not override `self.remainder` with an empty array
// Make sure to not overwrite `self.remainder` with an empty array
// when `next` is called after `ArrayChunks` exhaustion.
self.remainder.get_or_insert(remainder);

Expand Down
40 changes: 40 additions & 0 deletions core/src/iter/adapters/peekable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,8 @@ impl<I: Iterator> Peekable<I> {
/// If the closure panics, the next value will always be consumed and dropped
/// even if the panic is caught, because the closure never returned an `Err` value to put back.
///
/// See also: [`next_if_map_mut`](Self::next_if_map_mut).
///
/// # Examples
///
/// Parse the leading decimal number from an iterator of characters.
Expand Down Expand Up @@ -419,6 +421,44 @@ impl<I: Iterator> Peekable<I> {
self.peeked = Some(unpeek);
None
}

/// Gives a mutable reference to the next value of the iterator and applies a function `f` to it,
/// returning the result and advancing the iterator if `f` returns `Some`.
///
/// Otherwise, if `f` returns `None`, the next value is kept for the next iteration.
///
/// If `f` panics, the item that is consumed from the iterator as if `Some` was returned from `f`.
/// The value will be dropped.
///
/// This is similar to [`next_if_map`](Self::next_if_map), except ownership of the item is not given to `f`.
/// This can be preferable if `f` would copy the item anyway.
///
/// # Examples
///
/// Parse the leading decimal number from an iterator of characters.
/// ```
/// #![feature(peekable_next_if_map)]
/// let mut iter = "125 GOTO 10".chars().peekable();
/// let mut line_num = 0_u32;
/// while let Some(digit) = iter.next_if_map_mut(|c| c.to_digit(10)) {
/// line_num = line_num * 10 + digit;
/// }
/// assert_eq!(line_num, 125);
/// assert_eq!(iter.collect::<String>(), " GOTO 10");
/// ```
#[unstable(feature = "peekable_next_if_map", issue = "143702")]
pub fn next_if_map_mut<R>(&mut self, f: impl FnOnce(&mut I::Item) -> Option<R>) -> Option<R> {
let unpeek = if let Some(mut item) = self.next() {
match f(&mut item) {
Some(result) => return Some(result),
None => Some(item),
}
} else {
None
};
self.peeked = Some(unpeek);
None
}
}

#[unstable(feature = "trusted_len", issue = "37572")]
Expand Down
4 changes: 1 addition & 3 deletions core/src/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -898,8 +898,6 @@ pub const fn replace<T>(dest: &mut T, src: T) -> T {

/// Disposes of a value.
///
/// This does so by calling the argument's implementation of [`Drop`][drop].
///
/// This effectively does nothing for types which implement `Copy`, e.g.
/// integers. Such values are copied and _then_ moved into the function, so the
/// value persists after this function call.
Expand All @@ -910,7 +908,7 @@ pub const fn replace<T>(dest: &mut T, src: T) -> T {
/// pub fn drop<T>(_x: T) {}
/// ```
///
/// Because `_x` is moved into the function, it is automatically dropped before
/// Because `_x` is moved into the function, it is automatically [dropped][drop] before
/// the function returns.
///
/// [drop]: Drop
Expand Down
Loading
Loading