Auto merge of #151891 - jhpratt:rollup-cG0AjgK, r=jhpratt
Rollup of 5 pull requests Successful merges: - rust-lang/rust#143650 (core: add Option::get_or_try_insert_with) - rust-lang/rust#151726 (Remove duplicated code in `slice/index.rs`) - rust-lang/rust#151812 (Add `shift_{left,right}` on slices) - rust-lang/rust#151829 (Implement `BinaryHeap::pop_if()`) - rust-lang/rust#151838 (Fix typo for Maybe dangling docs)
This commit is contained in:
commit
1e9be1b77f
10 changed files with 344 additions and 41 deletions
|
|
@ -649,6 +649,33 @@ impl<T: Ord, A: Allocator> BinaryHeap<T, A> {
|
|||
})
|
||||
}
|
||||
|
||||
/// 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<T> {
|
||||
let first = self.peek()?;
|
||||
if predicate(first) { self.pop() } else { None }
|
||||
}
|
||||
|
||||
/// Pushes an item onto the binary heap.
|
||||
///
|
||||
/// # Examples
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#![feature(allocator_api)]
|
||||
#![feature(binary_heap_pop_if)]
|
||||
#![feature(const_heap)]
|
||||
#![feature(deque_extend_front)]
|
||||
#![feature(iter_array_chunks)]
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -773,7 +773,7 @@ impl<T: Copy> Bound<&T> {
|
|||
/// ```
|
||||
#[unstable(feature = "bound_copied", issue = "145966")]
|
||||
#[must_use]
|
||||
pub fn copied(self) -> Bound<T> {
|
||||
pub const fn copied(self) -> Bound<T> {
|
||||
match self {
|
||||
Bound::Unbounded => Bound::Unbounded,
|
||||
Bound::Included(x) => Bound::Included(*x),
|
||||
|
|
|
|||
|
|
@ -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<T> Option<T> {
|
|||
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<u32> = None;
|
||||
/// let mut o2: Option<u8> = 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,
|
||||
) -> <R::Residual as Residual<&'a mut T>>::TryType
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
R: Try<Output = T, Residual: Residual<&'a mut T>>,
|
||||
{
|
||||
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
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -910,29 +910,7 @@ where
|
|||
R: [const] ops::RangeBounds<usize> + [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<usize>,
|
||||
{
|
||||
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<usize>, ops::Bound<usize>),
|
||||
|
|
@ -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<usize>, ops::Bound<usize>),
|
||||
) -> ops::Range<usize> {
|
||||
|
|
|
|||
|
|
@ -3939,6 +3939,219 @@ impl<T> [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<const N: usize>(&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::<N>().read();
|
||||
ptr.copy_from(ptr.add(N), shift);
|
||||
ptr.add(shift).cast_array::<N>().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::<T>();
|
||||
|
||||
let mut returned = MaybeUninit::<[T; N]>::uninit();
|
||||
let ptr = returned.as_mut_ptr().cast::<T>();
|
||||
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<const N: usize>(&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::<N>().read();
|
||||
ptr.add(N).copy_from(ptr, shift);
|
||||
ptr.cast_array::<N>().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::<T>();
|
||||
|
||||
let mut returned = MaybeUninit::<[T; N]>::uninit();
|
||||
let ptr = returned.as_mut_ptr().cast::<T>();
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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<const M: usize, const N: usize>(
|
||||
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<const M: usize, const N: usize>(
|
||||
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]);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue