diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 5710b2a3a91d..97aafbc7b699 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -649,6 +649,33 @@ 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 + /// + /// ``` + /// #![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.as_slice(), [1]); + /// assert_eq!(heap.pop_if(pred), None); + /// assert_eq!(heap.as_slice(), [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.peek()?; + if predicate(first) { self.pop() } else { None } + } + /// Pushes an item onto the binary heap. /// /// # Examples diff --git a/library/alloctests/tests/collections/binary_heap.rs b/library/alloctests/tests/collections/binary_heap.rs index 95f4c3e614f5..e1484c32a4f8 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![0, 1, 2]); +} + #[test] fn test_peek_mut() { let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]; 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)] 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. 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/option.rs b/library/core/src/option.rs index b7c805f5855e..3dc53941977c 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -584,7 +584,7 @@ use crate::clone::TrivialClone; 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}; @@ -1816,6 +1816,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 ///////////////////////////////////////////////////////////////////////// diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index d8ed521f4435..59802989c18f 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().copied(), range.end_bound().copied())) } /// 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().copied(), range.end_bound().copied()))?; + 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 @@ -1011,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), @@ -1036,7 +1003,8 @@ 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( +#[inline] +pub(crate) const fn into_slice_range( len: usize, (start, end): (ops::Bound, ops::Bound), ) -> ops::Range { diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 429f90acec34..36dd4d6782ac 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 a24c83834edb..9054eada12fb 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -102,6 +102,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]); +}