From b8955c5656e0805847549ac6043d6ae89f7fc0cc Mon Sep 17 00:00:00 2001 From: binarycat Date: Tue, 8 Jul 2025 14:09:34 -0500 Subject: [PATCH 1/7] core: add Option::get_or_try_insert_with Co-authored-by: kennytm --- library/core/src/option.rs | 45 +++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index ed070fbd2274..da753f36b16a 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -578,7 +578,7 @@ use crate::iter::{self, FusedIterator, TrustedLen}; use crate::marker::Destruct; -use crate::ops::{self, ControlFlow, Deref, DerefMut}; +use crate::ops::{self, ControlFlow, Deref, DerefMut, Residual, Try}; use crate::panicking::{panic, panic_display}; use crate::pin::Pin; use crate::{cmp, convert, hint, mem, slice}; @@ -1807,6 +1807,49 @@ impl Option { unsafe { self.as_mut().unwrap_unchecked() } } + /// If the option is `None`, calls the closure and inserts its output if successful. + /// + /// If the closure returns a residual value such as `Err` or `None`, + /// that residual value is returned and nothing is inserted. + /// + /// If the option is `Some`, nothing is inserted. + /// + /// Unless a residual is returned, a mutable reference to the value + /// of the option will be output. + /// + /// # Examples + /// + /// ``` + /// #![feature(option_get_or_try_insert_with)] + /// let mut o1: Option = None; + /// let mut o2: Option = None; + /// + /// let number = "12345"; + /// + /// assert_eq!(o1.get_or_try_insert_with(|| number.parse()).copied(), Ok(12345)); + /// assert!(o2.get_or_try_insert_with(|| number.parse()).is_err()); + /// assert_eq!(o1, Some(12345)); + /// assert_eq!(o2, None); + /// ``` + #[inline] + #[unstable(feature = "option_get_or_try_insert_with", issue = "143648")] + pub fn get_or_try_insert_with<'a, R, F>( + &'a mut self, + f: F, + ) -> >::TryType + where + F: FnOnce() -> R, + R: Try>, + { + if let None = self { + *self = Some(f()?); + } + // SAFETY: a `None` variant for `self` would have been replaced by a `Some` + // variant in the code above. + + Try::from_output(unsafe { self.as_mut().unwrap_unchecked() }) + } + ///////////////////////////////////////////////////////////////////////// // Misc ///////////////////////////////////////////////////////////////////////// From 50a9b17d7c7d291ba1e8ad181a4e90e87247fd2f Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Mon, 26 Jan 2026 22:07:44 -0800 Subject: [PATCH 2/7] Remove duplicated code in `slice/index.rs` Looks like `const fn` is far enough along now that we can just not have these two copies any more, and have one call the other. --- library/core/src/slice/index.rs | 42 ++++----------------------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index d8ed521f4435..097310dc8fb0 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -910,29 +910,7 @@ where R: [const] ops::RangeBounds + [const] Destruct, { let len = bounds.end; - - let end = match range.end_bound() { - ops::Bound::Included(&end) if end >= len => slice_index_fail(0, end, len), - // Cannot overflow because `end < len` implies `end < usize::MAX`. - ops::Bound::Included(&end) => end + 1, - - ops::Bound::Excluded(&end) if end > len => slice_index_fail(0, end, len), - ops::Bound::Excluded(&end) => end, - ops::Bound::Unbounded => len, - }; - - let start = match range.start_bound() { - ops::Bound::Excluded(&start) if start >= end => slice_index_fail(start, end, len), - // Cannot overflow because `start < end` implies `start < usize::MAX`. - ops::Bound::Excluded(&start) => start + 1, - - ops::Bound::Included(&start) if start > end => slice_index_fail(start, end, len), - ops::Bound::Included(&start) => start, - - ops::Bound::Unbounded => 0, - }; - - ops::Range { start, end } + into_slice_range(len, (range.start_bound().cloned(), range.end_bound().cloned())) } /// Performs bounds checking of a range without panicking. @@ -972,20 +950,8 @@ where R: ops::RangeBounds, { let len = bounds.end; - - let start = match range.start_bound() { - ops::Bound::Included(&start) => start, - ops::Bound::Excluded(start) => start.checked_add(1)?, - ops::Bound::Unbounded => 0, - }; - - let end = match range.end_bound() { - ops::Bound::Included(end) => end.checked_add(1)?, - ops::Bound::Excluded(&end) => end, - ops::Bound::Unbounded => len, - }; - - if start > end || end > len { None } else { Some(ops::Range { start, end }) } + let r = into_range(len, (range.start_bound().cloned(), range.end_bound().cloned()))?; + if r.start > r.end || r.end > len { None } else { Some(r) } } /// Converts a pair of `ops::Bound`s into `ops::Range` without performing any @@ -1036,7 +1002,7 @@ pub(crate) const fn into_range( /// Converts pair of `ops::Bound`s into `ops::Range`. /// Panics on overflowing indices. -pub(crate) fn into_slice_range( +pub(crate) const fn into_slice_range( len: usize, (start, end): (ops::Bound, ops::Bound), ) -> ops::Range { From 9928723bffc9b3dae673462a78b97077a0bde200 Mon Sep 17 00:00:00 2001 From: Max Heller Date: Thu, 29 Jan 2026 10:20:34 -0500 Subject: [PATCH 3/7] Implement `BinaryHeap::pop_if()` --- .../alloc/src/collections/binary_heap/mod.rs | 26 +++++++++++++++++++ library/alloctests/lib.rs | 1 + .../tests/collections/binary_heap.rs | 12 +++++++++ 3 files changed, 39 insertions(+) diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 5710b2a3a91d..65c8d5213f16 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -649,6 +649,32 @@ impl BinaryHeap { }) } + /// Removes and returns the greatest item from the binary heap if the predicate + /// returns `true`, or [`None`] if the predicate returns false or the heap + /// is empty (the predicate will not be called in that case). + /// + /// # Examples + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::from([1, 2]); + /// let pred = |x: &i32| *x % 2 == 0; + /// + /// assert_eq!(heap.pop_if(pred), Some(2)); + /// assert_eq!(heap, BinaryHeap::from([1])); + /// assert_eq!(heap.pop_if(pred), None); + /// assert_eq!(heap, BinaryHeap::from([1])); + /// ``` + /// + /// # Time complexity + /// + /// The worst case cost of `pop_if` on a heap containing *n* elements is *O*(log(*n*)). + #[unstable(feature = "binary_heap_pop_if", issue = "151828")] + pub fn pop_if(&mut self, predicate: impl FnOnce(&T) -> bool) -> Option { + let first = self.data.first()?; + if predicate(first) { self.pop() } else { None } + } + /// Pushes an item onto the binary heap. /// /// # Examples diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs index fe14480102e3..9806c59ce0e1 100644 --- a/library/alloctests/lib.rs +++ b/library/alloctests/lib.rs @@ -17,6 +17,7 @@ #![feature(allocator_api)] #![feature(array_into_iter_constructors)] #![feature(assert_matches)] +#![feature(binary_heap_pop_if)] #![feature(box_vec_non_null)] #![feature(char_internals)] #![feature(const_alloc_error)] diff --git a/library/alloctests/tests/collections/binary_heap.rs b/library/alloctests/tests/collections/binary_heap.rs index 95f4c3e614f5..1b6afec7f355 100644 --- a/library/alloctests/tests/collections/binary_heap.rs +++ b/library/alloctests/tests/collections/binary_heap.rs @@ -136,6 +136,18 @@ fn test_peek_and_pop() { } } +#[test] +fn test_pop_if() { + let data = vec![9, 8, 7, 6, 5, 4, 3, 2, 1, 0]; + let mut sorted = data.clone(); + sorted.sort(); + let mut heap = BinaryHeap::from(data); + while let Some(popped) = heap.pop_if(|x| *x > 2) { + assert_eq!(popped, sorted.pop().unwrap()); + } + assert_eq!(heap.into_sorted_vec(), vec![1, 2]); +} + #[test] fn test_peek_mut() { let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]; From 959595fb45ac706b1a2c4e7774f69d18ae6441f3 Mon Sep 17 00:00:00 2001 From: IonicMC Date: Thu, 29 Jan 2026 20:26:06 +0200 Subject: [PATCH 4/7] Fix typo for Maybe dangling docs Boxe's -> Box's --- library/core/src/mem/maybe_dangling.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/mem/maybe_dangling.rs b/library/core/src/mem/maybe_dangling.rs index 3c5437757e97..a5f77e667f97 100644 --- a/library/core/src/mem/maybe_dangling.rs +++ b/library/core/src/mem/maybe_dangling.rs @@ -29,7 +29,7 @@ use crate::{mem, ptr}; /// mem::forget(boxed); // <-- this is UB! /// ``` /// -/// Even though the `Box`e's destructor is not run (and thus we don't have a double free bug), this +/// Even though the `Box`'s destructor is not run (and thus we don't have a double free bug), this /// code is still UB. This is because when moving `boxed` into `forget`, its validity invariants /// are asserted, causing UB since the `Box` is dangling. The safety comment is as such wrong, as /// moving the `boxed` variable as part of the `forget` call *is* a use. From 4264da6869bfbd8a314990dcf567389ab0e416af Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 29 Jan 2026 01:27:22 -0800 Subject: [PATCH 5/7] Add `shift_{left,right}` on slices cc tracking issue 151772 --- library/core/src/slice/mod.rs | 213 +++++++++++++++++++++++++++++++ library/coretests/tests/lib.rs | 1 + library/coretests/tests/slice.rs | 38 ++++++ 3 files changed, 252 insertions(+) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 3e1eeba4e92e..e449ceadf19d 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -3939,6 +3939,219 @@ impl [T] { } } + /// Moves the elements of this slice `N` places to the left, returning the ones + /// that "fall off" the front, and putting `inserted` at the end. + /// + /// Equivalently, you can think of concatenating `self` and `inserted` into one + /// long sequence, then returning the left-most `N` items and the rest into `self`: + /// + /// ```text + /// self (before) inserted + /// vvvvvvvvvvvvvvv vvv + /// [1, 2, 3, 4, 5] [9] + /// ↙ ↙ ↙ ↙ ↙ ↙ + /// [1] [2, 3, 4, 5, 9] + /// ^^^ ^^^^^^^^^^^^^^^ + /// returned self (after) + /// ``` + /// + /// See also [`Self::shift_right`] and compare [`Self::rotate_left`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_shift)] + /// + /// // Same as the diagram above + /// let mut a = [1, 2, 3, 4, 5]; + /// let inserted = [9]; + /// let returned = a.shift_left(inserted); + /// assert_eq!(returned, [1]); + /// assert_eq!(a, [2, 3, 4, 5, 9]); + /// + /// // You can shift multiple items at a time + /// let mut a = *b"Hello world"; + /// assert_eq!(a.shift_left(*b" peace"), *b"Hello "); + /// assert_eq!(a, *b"world peace"); + /// + /// // The name comes from this operation's similarity to bitshifts + /// let mut a: u8 = 0b10010110; + /// a <<= 3; + /// assert_eq!(a, 0b10110000_u8); + /// let mut a: [_; 8] = [1, 0, 0, 1, 0, 1, 1, 0]; + /// a.shift_left([0; 3]); + /// assert_eq!(a, [1, 0, 1, 1, 0, 0, 0, 0]); + /// + /// // Remember you can sub-slice to affect less that the whole slice. + /// // For example, this is similar to `.remove(1)` + `.insert(4, 'Z')` + /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; + /// assert_eq!(a[1..=4].shift_left(['Z']), ['b']); + /// assert_eq!(a, ['a', 'c', 'd', 'e', 'Z', 'f']); + /// + /// // If the size matches it's equivalent to `mem::replace` + /// let mut a = [1, 2, 3]; + /// assert_eq!(a.shift_left([7, 8, 9]), [1, 2, 3]); + /// assert_eq!(a, [7, 8, 9]); + /// + /// // Some of the "inserted" elements end up returned if the slice is too short + /// let mut a = []; + /// assert_eq!(a.shift_left([1, 2, 3]), [1, 2, 3]); + /// let mut a = [9]; + /// assert_eq!(a.shift_left([1, 2, 3]), [9, 1, 2]); + /// assert_eq!(a, [3]); + /// ``` + #[unstable(feature = "slice_shift", issue = "151772")] + pub const fn shift_left(&mut self, inserted: [T; N]) -> [T; N] { + if let Some(shift) = self.len().checked_sub(N) { + // SAFETY: Having just checked that the inserted/returned arrays are + // shorter than (or the same length as) the slice: + // 1. The read for the items to return is in-bounds + // 2. We can `memmove` the slice over to cover the items we're returning + // to ensure those aren't double-dropped + // 3. Then we write (in-bounds for the same reason as the read) the + // inserted items atop the items of the slice that we just duplicated + // + // And none of this can panic, so there's no risk of intermediate unwinds. + unsafe { + let ptr = self.as_mut_ptr(); + let returned = ptr.cast_array::().read(); + ptr.copy_from(ptr.add(N), shift); + ptr.add(shift).cast_array::().write(inserted); + returned + } + } else { + // SAFETY: Having checked that the slice is strictly shorter than the + // inserted/returned arrays, it means we'll be copying the whole slice + // into the returned array, but that's not enough on its own. We also + // need to copy some of the inserted array into the returned array, + // with the rest going into the slice. Because `&mut` is exclusive + // and we own both `inserted` and `returned`, they're all disjoint + // allocations from each other as we can use `nonoverlapping` copies. + // + // We avoid double-frees by `ManuallyDrop`ing the inserted items, + // since we always copy them to other locations that will drop them + // instead. Plus nothing in here can panic -- it's just memcpy three + // times -- so there's no intermediate unwinds to worry about. + unsafe { + let len = self.len(); + let slice = self.as_mut_ptr(); + let inserted = mem::ManuallyDrop::new(inserted); + let inserted = (&raw const inserted).cast::(); + + let mut returned = MaybeUninit::<[T; N]>::uninit(); + let ptr = returned.as_mut_ptr().cast::(); + ptr.copy_from_nonoverlapping(slice, len); + ptr.add(len).copy_from_nonoverlapping(inserted, N - len); + slice.copy_from_nonoverlapping(inserted.add(N - len), len); + returned.assume_init() + } + } + } + + /// Moves the elements of this slice `N` places to the right, returning the ones + /// that "fall off" the back, and putting `inserted` at the beginning. + /// + /// Equivalently, you can think of concatenating `inserted` and `self` into one + /// long sequence, then returning the right-most `N` items and the rest into `self`: + /// + /// ```text + /// inserted self (before) + /// vvv vvvvvvvvvvvvvvv + /// [0] [5, 6, 7, 8, 9] + /// ↘ ↘ ↘ ↘ ↘ ↘ + /// [0, 5, 6, 7, 8] [9] + /// ^^^^^^^^^^^^^^^ ^^^ + /// self (after) returned + /// ``` + /// + /// See also [`Self::shift_left`] and compare [`Self::rotate_right`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_shift)] + /// + /// // Same as the diagram above + /// let mut a = [5, 6, 7, 8, 9]; + /// let inserted = [0]; + /// let returned = a.shift_right(inserted); + /// assert_eq!(returned, [9]); + /// assert_eq!(a, [0, 5, 6, 7, 8]); + /// + /// // The name comes from this operation's similarity to bitshifts + /// let mut a: u8 = 0b10010110; + /// a >>= 3; + /// assert_eq!(a, 0b00010010_u8); + /// let mut a: [_; 8] = [1, 0, 0, 1, 0, 1, 1, 0]; + /// a.shift_right([0; 3]); + /// assert_eq!(a, [0, 0, 0, 1, 0, 0, 1, 0]); + /// + /// // Remember you can sub-slice to affect less that the whole slice. + /// // For example, this is similar to `.remove(4)` + `.insert(1, 'Z')` + /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; + /// assert_eq!(a[1..=4].shift_right(['Z']), ['e']); + /// assert_eq!(a, ['a', 'Z', 'b', 'c', 'd', 'f']); + /// + /// // If the size matches it's equivalent to `mem::replace` + /// let mut a = [1, 2, 3]; + /// assert_eq!(a.shift_right([7, 8, 9]), [1, 2, 3]); + /// assert_eq!(a, [7, 8, 9]); + /// + /// // Some of the "inserted" elements end up returned if the slice is too short + /// let mut a = []; + /// assert_eq!(a.shift_right([1, 2, 3]), [1, 2, 3]); + /// let mut a = [9]; + /// assert_eq!(a.shift_right([1, 2, 3]), [2, 3, 9]); + /// assert_eq!(a, [1]); + /// ``` + #[unstable(feature = "slice_shift", issue = "151772")] + pub const fn shift_right(&mut self, inserted: [T; N]) -> [T; N] { + if let Some(shift) = self.len().checked_sub(N) { + // SAFETY: Having just checked that the inserted/returned arrays are + // shorter than (or the same length as) the slice: + // 1. The read for the items to return is in-bounds + // 2. We can `memmove` the slice over to cover the items we're returning + // to ensure those aren't double-dropped + // 3. Then we write (in-bounds for the same reason as the read) the + // inserted items atop the items of the slice that we just duplicated + // + // And none of this can panic, so there's no risk of intermediate unwinds. + unsafe { + let ptr = self.as_mut_ptr(); + let returned = ptr.add(shift).cast_array::().read(); + ptr.add(N).copy_from(ptr, shift); + ptr.cast_array::().write(inserted); + returned + } + } else { + // SAFETY: Having checked that the slice is strictly shorter than the + // inserted/returned arrays, it means we'll be copying the whole slice + // into the returned array, but that's not enough on its own. We also + // need to copy some of the inserted array into the returned array, + // with the rest going into the slice. Because `&mut` is exclusive + // and we own both `inserted` and `returned`, they're all disjoint + // allocations from each other as we can use `nonoverlapping` copies. + // + // We avoid double-frees by `ManuallyDrop`ing the inserted items, + // since we always copy them to other locations that will drop them + // instead. Plus nothing in here can panic -- it's just memcpy three + // times -- so there's no intermediate unwinds to worry about. + unsafe { + let len = self.len(); + let slice = self.as_mut_ptr(); + let inserted = mem::ManuallyDrop::new(inserted); + let inserted = (&raw const inserted).cast::(); + + let mut returned = MaybeUninit::<[T; N]>::uninit(); + let ptr = returned.as_mut_ptr().cast::(); + ptr.add(N - len).copy_from_nonoverlapping(slice, len); + ptr.copy_from_nonoverlapping(inserted.add(len), N - len); + slice.copy_from_nonoverlapping(inserted, len); + returned.assume_init() + } + } + } + /// Fills `self` with elements by cloning `value`. /// /// # Examples diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 8cca714b7393..b6dd130aa35f 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -101,6 +101,7 @@ #![feature(slice_index_methods)] #![feature(slice_internals)] #![feature(slice_partition_dedup)] +#![feature(slice_shift)] #![feature(slice_split_once)] #![feature(sliceindex_wrappers)] #![feature(split_array)] diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs index 6f60f71e8a47..2bb62f36bb0e 100644 --- a/library/coretests/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -2507,3 +2507,41 @@ fn test_slice_from_raw_parts_in_const() { assert_eq!(EMPTY_SLICE.as_ptr().addr(), 123456); assert_eq!(EMPTY_SLICE.len(), 0); } + +#[test] +fn test_shift_left() { + #[track_caller] + fn case( + mut a: [i32; M], + i: [i32; N], + j: [i32; N], + b: [i32; M], + ) { + assert_eq!((a.shift_left(i), a), (j, b)); + } + case([], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], []); + case([1], [2, 3, 4, 5], [1, 2, 3, 4], [5]); + case([1, 2], [3, 4, 5], [1, 2, 3], [4, 5]); + case([1, 2, 3], [4, 5], [1, 2], [3, 4, 5]); + case([1, 2, 3, 4], [5], [1], [2, 3, 4, 5]); + case([1, 2, 3, 4, 5], [], [], [1, 2, 3, 4, 5]); +} + +#[test] +fn test_shift_right() { + #[track_caller] + fn case( + i: [i32; N], + mut a: [i32; M], + b: [i32; M], + j: [i32; N], + ) { + assert_eq!((a.shift_right(i), a), (j, b)); + } + case([], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], []); + case([1], [2, 3, 4, 5], [1, 2, 3, 4], [5]); + case([1, 2], [3, 4, 5], [1, 2, 3], [4, 5]); + case([1, 2, 3], [4, 5], [1, 2], [3, 4, 5]); + case([1, 2, 3, 4], [5], [1], [2, 3, 4, 5]); + case([1, 2, 3, 4, 5], [], [], [1, 2, 3, 4, 5]); +} From bae7a199f1cd87052396f65b59b51946eae29db1 Mon Sep 17 00:00:00 2001 From: Max Heller Date: Fri, 30 Jan 2026 09:55:34 -0500 Subject: [PATCH 6/7] Address review comments and fix tests --- library/alloc/src/collections/binary_heap/mod.rs | 7 ++++--- library/alloctests/lib.rs | 1 - library/alloctests/tests/collections/binary_heap.rs | 2 +- library/alloctests/tests/lib.rs | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 65c8d5213f16..97aafbc7b699 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -656,14 +656,15 @@ impl BinaryHeap { /// # Examples /// /// ``` + /// #![feature(binary_heap_pop_if)] /// use std::collections::BinaryHeap; /// let mut heap = BinaryHeap::from([1, 2]); /// let pred = |x: &i32| *x % 2 == 0; /// /// assert_eq!(heap.pop_if(pred), Some(2)); - /// assert_eq!(heap, BinaryHeap::from([1])); + /// assert_eq!(heap.as_slice(), [1]); /// assert_eq!(heap.pop_if(pred), None); - /// assert_eq!(heap, BinaryHeap::from([1])); + /// assert_eq!(heap.as_slice(), [1]); /// ``` /// /// # Time complexity @@ -671,7 +672,7 @@ impl BinaryHeap { /// The worst case cost of `pop_if` on a heap containing *n* elements is *O*(log(*n*)). #[unstable(feature = "binary_heap_pop_if", issue = "151828")] pub fn pop_if(&mut self, predicate: impl FnOnce(&T) -> bool) -> Option { - let first = self.data.first()?; + let first = self.peek()?; if predicate(first) { self.pop() } else { None } } diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs index 9806c59ce0e1..fe14480102e3 100644 --- a/library/alloctests/lib.rs +++ b/library/alloctests/lib.rs @@ -17,7 +17,6 @@ #![feature(allocator_api)] #![feature(array_into_iter_constructors)] #![feature(assert_matches)] -#![feature(binary_heap_pop_if)] #![feature(box_vec_non_null)] #![feature(char_internals)] #![feature(const_alloc_error)] diff --git a/library/alloctests/tests/collections/binary_heap.rs b/library/alloctests/tests/collections/binary_heap.rs index 1b6afec7f355..e1484c32a4f8 100644 --- a/library/alloctests/tests/collections/binary_heap.rs +++ b/library/alloctests/tests/collections/binary_heap.rs @@ -145,7 +145,7 @@ fn test_pop_if() { while let Some(popped) = heap.pop_if(|x| *x > 2) { assert_eq!(popped, sorted.pop().unwrap()); } - assert_eq!(heap.into_sorted_vec(), vec![1, 2]); + assert_eq!(heap.into_sorted_vec(), vec![0, 1, 2]); } #[test] diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 2926248edbf5..e15c86496cf1 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -1,4 +1,5 @@ #![feature(allocator_api)] +#![feature(binary_heap_pop_if)] #![feature(const_heap)] #![feature(deque_extend_front)] #![feature(iter_array_chunks)] From a3f169c75b7625e15e07e4ed7855647871207fd1 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Tue, 27 Jan 2026 08:46:08 -0800 Subject: [PATCH 7/7] Use `Bound::copied` instead of `Bound::cloned` --- library/core/src/ops/range.rs | 2 +- library/core/src/slice/index.rs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index a0b74ff383ea..c15c8f20c16b 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -773,7 +773,7 @@ impl Bound<&T> { /// ``` #[unstable(feature = "bound_copied", issue = "145966")] #[must_use] - pub fn copied(self) -> Bound { + pub const fn copied(self) -> Bound { match self { Bound::Unbounded => Bound::Unbounded, Bound::Included(x) => Bound::Included(*x), diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 097310dc8fb0..59802989c18f 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -910,7 +910,7 @@ where R: [const] ops::RangeBounds + [const] Destruct, { let len = bounds.end; - into_slice_range(len, (range.start_bound().cloned(), range.end_bound().cloned())) + into_slice_range(len, (range.start_bound().copied(), range.end_bound().copied())) } /// Performs bounds checking of a range without panicking. @@ -950,7 +950,7 @@ where R: ops::RangeBounds, { let len = bounds.end; - let r = into_range(len, (range.start_bound().cloned(), range.end_bound().cloned()))?; + let r = into_range(len, (range.start_bound().copied(), range.end_bound().copied()))?; if r.start > r.end || r.end > len { None } else { Some(r) } } @@ -977,6 +977,7 @@ pub(crate) const fn into_range_unchecked( /// Converts pair of `ops::Bound`s into `ops::Range`. /// Returns `None` on overflowing indices. #[rustc_const_unstable(feature = "const_range", issue = "none")] +#[inline] pub(crate) const fn into_range( len: usize, (start, end): (ops::Bound, ops::Bound), @@ -1002,6 +1003,7 @@ pub(crate) const fn into_range( /// Converts pair of `ops::Bound`s into `ops::Range`. /// Panics on overflowing indices. +#[inline] pub(crate) const fn into_slice_range( len: usize, (start, end): (ops::Bound, ops::Bound),