From 188c40126dbda835e80238ea70eaef7d09e3e167 Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Wed, 4 Jun 2025 18:15:12 -0400 Subject: [PATCH 1/4] add Vec::peek_mut --- library/alloc/src/vec/mod.rs | 35 ++++++++++++++++++++ library/alloc/src/vec/peek_mut.rs | 55 +++++++++++++++++++++++++++++++ library/alloctests/lib.rs | 1 + library/alloctests/tests/vec.rs | 11 +++++++ 4 files changed, 102 insertions(+) create mode 100644 library/alloc/src/vec/peek_mut.rs diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index ce7321544b6b9..8763ce674be89 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -109,6 +109,11 @@ mod in_place_collect; mod partial_eq; +#[unstable(feature = "vec_peek_mut", issue = "122742")] +pub use self::peek_mut::PeekMut; + +mod peek_mut; + #[cfg(not(no_global_oom_handling))] use self::spec_from_elem::SpecFromElem; @@ -729,6 +734,36 @@ impl Vec { pub unsafe fn from_parts(ptr: NonNull, length: usize, capacity: usize) -> Self { unsafe { Self::from_parts_in(ptr, length, capacity, Global) } } + + /// Returns a mutable reference to the greatest item in the binary heap, or + /// `None` if it is empty. + /// + /// Note: If the `PeekMut` value is leaked, some heap elements might get + /// leaked along with it, but the remaining elements will remain a valid + /// heap. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut vec = Vec::new(); + /// assert!(vec.peek_mut().is_none()); + /// + /// vec.push(1); + /// vec.push(5); + /// vec.push(2); + /// assert_eq!(vec.last(), Some(&2)); + /// if let Some(mut val) = vec.peek_mut() { + /// *val = 0; + /// } + /// assert_eq!(vec.last(), Some(&0)); + /// ``` + #[inline] + #[unstable(feature = "vec_peek_mut", issue = "122742")] + pub fn peek_mut(&mut self) -> Option> { + PeekMut::new(self) + } } impl Vec { diff --git a/library/alloc/src/vec/peek_mut.rs b/library/alloc/src/vec/peek_mut.rs new file mode 100644 index 0000000000000..c0dd941ed3933 --- /dev/null +++ b/library/alloc/src/vec/peek_mut.rs @@ -0,0 +1,55 @@ +use core::ops::{Deref, DerefMut}; + +use super::Vec; +use crate::fmt; + +/// Structure wrapping a mutable reference to the last item in a +/// `Vec`. +/// +/// This `struct` is created by the [`peek_mut`] method on [`Vec`]. See +/// its documentation for more. +/// +/// [`peek_mut`]: Vec::peek_mut +#[unstable(feature = "vec_peek_mut", issue = "122742")] +pub struct PeekMut<'a, T> { + vec: &'a mut Vec, +} + +#[unstable(feature = "vec_peek_mut", issue = "122742")] +impl fmt::Debug for PeekMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("PeekMut").field(self.deref()).finish() + } +} + +impl<'a, T> PeekMut<'a, T> { + pub(crate) fn new(vec: &'a mut Vec) -> Option { + if vec.is_empty() { None } else { Some(Self { vec }) } + } + + /// Removes the peeked value from the vector and returns it. + #[unstable(feature = "vec_peek_mut", issue = "122742")] + pub fn pop(self) -> T { + // SAFETY: PeekMut is only constructed if the vec is non-empty + unsafe { self.vec.pop().unwrap_unchecked() } + } +} + +#[unstable(feature = "vec_peek_mut", issue = "122742")] +impl<'a, T> Deref for PeekMut<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + // SAFETY: PeekMut is only constructed if the vec is non-empty + unsafe { self.vec.get_unchecked(self.vec.len() - 1) } + } +} + +#[unstable(feature = "vec_peek_mut", issue = "122742")] +impl<'a, T> DerefMut for PeekMut<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + let idx = self.vec.len() - 1; + // SAFETY: PeekMut is only constructed if the vec is non-empty + unsafe { self.vec.get_unchecked_mut(idx) } + } +} diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs index 56e60ed4c8448..232cf06fff9b6 100644 --- a/library/alloctests/lib.rs +++ b/library/alloctests/lib.rs @@ -42,6 +42,7 @@ #![feature(trusted_random_access)] #![feature(try_reserve_kind)] #![feature(try_trait_v2)] +#![feature(vec_peek_mut)] // tidy-alphabetical-end // // Language features: diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs index f430d979fa848..0de9da3756180 100644 --- a/library/alloctests/tests/vec.rs +++ b/library/alloctests/tests/vec.rs @@ -2698,6 +2698,17 @@ fn test_pop_if_mutates() { assert_eq!(v, [2]); } +#[test] +fn test_peek_mut() { + let mut vec = Vec::new(); + assert!(vec.peek_mut().is_none()); + vec.push(1); + vec.push(2); + assert_eq!(vec.peek_mut(), Some(2)); + *vec.peek_mut() = 0; + assert_eq!(vec.peek_mut(), Some(0)); +} + /// This assortment of tests, in combination with miri, verifies we handle UB on fishy arguments /// in the stdlib. Draining and extending the allocation are fairly well-tested earlier, but /// `vec.insert(usize::MAX, val)` once slipped by! From cc8042902119267066017d8b5ba63552a990f46d Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Fri, 6 Jun 2025 13:07:24 -0400 Subject: [PATCH 2/4] fix tests --- library/alloc/src/vec/mod.rs | 1 + library/alloctests/lib.rs | 1 - library/alloctests/tests/lib.rs | 1 + library/alloctests/tests/vec.rs | 10 +++++++--- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 8763ce674be89..96d082aba223d 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -747,6 +747,7 @@ impl Vec { /// Basic usage: /// /// ``` + /// #![feature(vec_peek_mut)] /// let mut vec = Vec::new(); /// assert!(vec.peek_mut().is_none()); /// diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs index 232cf06fff9b6..56e60ed4c8448 100644 --- a/library/alloctests/lib.rs +++ b/library/alloctests/lib.rs @@ -42,7 +42,6 @@ #![feature(trusted_random_access)] #![feature(try_reserve_kind)] #![feature(try_trait_v2)] -#![feature(vec_peek_mut)] // tidy-alphabetical-end // // Language features: diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 38309585fad61..a41162ecd51a0 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -40,6 +40,7 @@ #![feature(vec_deque_truncate_front)] #![feature(unique_rc_arc)] #![feature(macro_metavar_expr_concat)] +#![feature(vec_peek_mut)] #![allow(internal_features)] #![deny(fuzzy_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs index 0de9da3756180..adbd4ccb8972a 100644 --- a/library/alloctests/tests/vec.rs +++ b/library/alloctests/tests/vec.rs @@ -2704,9 +2704,13 @@ fn test_peek_mut() { assert!(vec.peek_mut().is_none()); vec.push(1); vec.push(2); - assert_eq!(vec.peek_mut(), Some(2)); - *vec.peek_mut() = 0; - assert_eq!(vec.peek_mut(), Some(0)); + if let Some(mut p) = vec.peek_mut() { + assert_eq!(*p, 2); + *p = 0; + assert_eq!(*p, 0); + } else { + unreachable!() + } } /// This assortment of tests, in combination with miri, verifies we handle UB on fishy arguments From 6469eb1c28bd6fd5fbc94cec1828fc69b00421d5 Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Fri, 6 Jun 2025 21:27:37 -0400 Subject: [PATCH 3/4] bless test stderr --- tests/ui/borrowck/issue-47646.stderr | 2 +- tests/ui/borrowck/issue-85581.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/borrowck/issue-47646.stderr b/tests/ui/borrowck/issue-47646.stderr index 85adfc03d1070..cfe6f3f399388 100644 --- a/tests/ui/borrowck/issue-47646.stderr +++ b/tests/ui/borrowck/issue-47646.stderr @@ -11,7 +11,7 @@ LL | println!("{:?}", heap); | ^^^^ immutable borrow occurs here ... LL | }; - | - ... and the mutable borrow might be used here, when that temporary is dropped and runs the destructor for type `(Option>, ())` + | - ... and the mutable borrow might be used here, when that temporary is dropped and runs the destructor for type `(Option>, ())` | = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/borrowck/issue-85581.stderr b/tests/ui/borrowck/issue-85581.stderr index 80f1f4cb50919..5fd457eb8dd45 100644 --- a/tests/ui/borrowck/issue-85581.stderr +++ b/tests/ui/borrowck/issue-85581.stderr @@ -10,7 +10,7 @@ LL | Some(_) => { heap.pop(); }, | ^^^^ second mutable borrow occurs here ... LL | } - | - ... and the first borrow might be used here, when that temporary is dropped and runs the destructor for type `Option>` + | - ... and the first borrow might be used here, when that temporary is dropped and runs the destructor for type `Option>` error: aborting due to 1 previous error From 9d19cbe29ba9609db1cf2332f5aa343a28a0e8dd Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Wed, 11 Jun 2025 22:57:57 -0400 Subject: [PATCH 4/4] update docs, test --- library/alloc/src/vec/mod.rs | 6 +----- library/alloctests/tests/vec.rs | 2 ++ 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 96d082aba223d..5bd82560da7ed 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -735,13 +735,9 @@ impl Vec { unsafe { Self::from_parts_in(ptr, length, capacity, Global) } } - /// Returns a mutable reference to the greatest item in the binary heap, or + /// Returns a mutable reference to the last item in the vector, or /// `None` if it is empty. /// - /// Note: If the `PeekMut` value is leaked, some heap elements might get - /// leaked along with it, but the remaining elements will remain a valid - /// heap. - /// /// # Examples /// /// Basic usage: diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs index adbd4ccb8972a..51b49b8edb3f0 100644 --- a/library/alloctests/tests/vec.rs +++ b/library/alloctests/tests/vec.rs @@ -2708,6 +2708,8 @@ fn test_peek_mut() { assert_eq!(*p, 2); *p = 0; assert_eq!(*p, 0); + p.pop(); + assert_eq!(vec.len(), 1); } else { unreachable!() }