Rollup merge of #146260 - Qelxiros:146179-sliceindex-wrappers, r=jhpratt

add SliceIndex wrapper types Last and Clamp<Idx>

Tracking issue: rust-lang/rust#146179
This commit is contained in:
Matthias Krüger 2025-11-03 06:54:35 +01:00 committed by GitHub
commit 01ab3e369b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 577 additions and 0 deletions

472
library/core/src/index.rs Normal file
View file

@ -0,0 +1,472 @@
#![unstable(feature = "sliceindex_wrappers", issue = "146179")]
//! Helper types for indexing slices.
use crate::intrinsics::slice_get_unchecked;
use crate::slice::SliceIndex;
use crate::{cmp, ops, range};
/// Clamps an index, guaranteeing that it will only access valid elements of the slice.
///
/// # Examples
///
/// ```
/// #![feature(sliceindex_wrappers)]
///
/// use core::index::Clamp;
///
/// let s: &[usize] = &[0, 1, 2, 3];
///
/// assert_eq!(&3, &s[Clamp(6)]);
/// assert_eq!(&[1, 2, 3], &s[Clamp(1..6)]);
/// assert_eq!(&[] as &[usize], &s[Clamp(5..6)]);
/// assert_eq!(&[0, 1, 2, 3], &s[Clamp(..6)]);
/// assert_eq!(&[0, 1, 2, 3], &s[Clamp(..=6)]);
/// assert_eq!(&[] as &[usize], &s[Clamp(6..)]);
/// ```
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
#[derive(Debug)]
pub struct Clamp<Idx>(pub Idx);
/// Always accesses the last element of the slice.
///
/// # Examples
///
/// ```
/// #![feature(sliceindex_wrappers)]
/// #![feature(slice_index_methods)]
///
/// use core::index::Last;
/// use core::slice::SliceIndex;
///
/// let s = &[0, 1, 2, 3];
///
/// assert_eq!(&3, &s[Last]);
/// assert_eq!(None, Last.get(&[] as &[usize]));
///
/// ```
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
#[derive(Debug)]
pub struct Last;
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
unsafe impl<T> SliceIndex<[T]> for Clamp<usize> {
type Output = T;
fn get(self, slice: &[T]) -> Option<&Self::Output> {
slice.get(cmp::min(self.0, slice.len() - 1))
}
fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> {
slice.get_mut(cmp::min(self.0, slice.len() - 1))
}
unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output {
// SAFETY: the caller ensures that the slice isn't empty
unsafe { slice_get_unchecked(slice, cmp::min(self.0, slice.len() - 1)) }
}
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output {
// SAFETY: the caller ensures that the slice isn't empty
unsafe { slice_get_unchecked(slice, cmp::min(self.0, slice.len() - 1)) }
}
fn index(self, slice: &[T]) -> &Self::Output {
&(*slice)[cmp::min(self.0, slice.len() - 1)]
}
fn index_mut(self, slice: &mut [T]) -> &mut Self::Output {
&mut (*slice)[cmp::min(self.0, slice.len() - 1)]
}
}
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
unsafe impl<T> SliceIndex<[T]> for Clamp<range::Range<usize>> {
type Output = [T];
fn get(self, slice: &[T]) -> Option<&Self::Output> {
let start = cmp::min(self.0.start, slice.len());
let end = cmp::min(self.0.end, slice.len());
(start..end).get(slice)
}
fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> {
let start = cmp::min(self.0.start, slice.len());
let end = cmp::min(self.0.end, slice.len());
(start..end).get_mut(slice)
}
unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output {
let start = cmp::min(self.0.start, slice.len());
let end = cmp::min(self.0.end, slice.len());
// SAFETY: a range ending before len is always valid
unsafe { (start..end).get_unchecked(slice) }
}
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output {
let start = cmp::min(self.0.start, slice.len());
let end = cmp::min(self.0.end, slice.len());
// SAFETY: a range ending before len is always valid
unsafe { (start..end).get_unchecked_mut(slice) }
}
fn index(self, slice: &[T]) -> &Self::Output {
let start = cmp::min(self.0.start, slice.len());
let end = cmp::min(self.0.end, slice.len());
(start..end).index(slice)
}
fn index_mut(self, slice: &mut [T]) -> &mut Self::Output {
let start = cmp::min(self.0.start, slice.len());
let end = cmp::min(self.0.end, slice.len());
(start..end).index_mut(slice)
}
}
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
unsafe impl<T> SliceIndex<[T]> for Clamp<ops::Range<usize>> {
type Output = [T];
fn get(self, slice: &[T]) -> Option<&Self::Output> {
let start = cmp::min(self.0.start, slice.len());
let end = cmp::min(self.0.end, slice.len());
(start..end).get(slice)
}
fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> {
let start = cmp::min(self.0.start, slice.len());
let end = cmp::min(self.0.end, slice.len());
(start..end).get_mut(slice)
}
unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output {
let start = cmp::min(self.0.start, slice.len());
let end = cmp::min(self.0.end, slice.len());
// SAFETY: a range ending before len is always valid
unsafe { (start..end).get_unchecked(slice) }
}
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output {
let start = cmp::min(self.0.start, slice.len());
let end = cmp::min(self.0.end, slice.len());
// SAFETY: a range ending before len is always valid
unsafe { (start..end).get_unchecked_mut(slice) }
}
fn index(self, slice: &[T]) -> &Self::Output {
let start = cmp::min(self.0.start, slice.len());
let end = cmp::min(self.0.end, slice.len());
(start..end).index(slice)
}
fn index_mut(self, slice: &mut [T]) -> &mut Self::Output {
let start = cmp::min(self.0.start, slice.len());
let end = cmp::min(self.0.end, slice.len());
(start..end).index_mut(slice)
}
}
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
unsafe impl<T> SliceIndex<[T]> for Clamp<range::RangeInclusive<usize>> {
type Output = [T];
fn get(self, slice: &[T]) -> Option<&Self::Output> {
let start = cmp::min(self.0.start, slice.len() - 1);
let end = cmp::min(self.0.last, slice.len() - 1);
(start..=end).get(slice)
}
fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> {
let start = cmp::min(self.0.start, slice.len() - 1);
let end = cmp::min(self.0.last, slice.len() - 1);
(start..=end).get_mut(slice)
}
unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output {
let start = cmp::min(self.0.start, slice.len() - 1);
let end = cmp::min(self.0.last, slice.len() - 1);
// SAFETY: the caller ensures that the slice isn't empty
unsafe { (start..=end).get_unchecked(slice) }
}
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output {
let start = cmp::min(self.0.start, slice.len() - 1);
let end = cmp::min(self.0.last, slice.len() - 1);
// SAFETY: the caller ensures that the slice isn't empty
unsafe { (start..=end).get_unchecked_mut(slice) }
}
fn index(self, slice: &[T]) -> &Self::Output {
let start = cmp::min(self.0.start, slice.len() - 1);
let end = cmp::min(self.0.last, slice.len() - 1);
(start..=end).index(slice)
}
fn index_mut(self, slice: &mut [T]) -> &mut Self::Output {
let start = cmp::min(self.0.start, slice.len() - 1);
let end = cmp::min(self.0.last, slice.len() - 1);
(start..=end).index_mut(slice)
}
}
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
unsafe impl<T> SliceIndex<[T]> for Clamp<ops::RangeInclusive<usize>> {
type Output = [T];
fn get(self, slice: &[T]) -> Option<&Self::Output> {
let start = cmp::min(self.0.start, slice.len() - 1);
let end = cmp::min(self.0.end, slice.len() - 1);
(start..=end).get(slice)
}
fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> {
let start = cmp::min(self.0.start, slice.len() - 1);
let end = cmp::min(self.0.end, slice.len() - 1);
(start..=end).get_mut(slice)
}
unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output {
let start = cmp::min(self.0.start, slice.len() - 1);
let end = cmp::min(self.0.end, slice.len() - 1);
// SAFETY: the caller ensures that the slice isn't empty
unsafe { (start..=end).get_unchecked(slice) }
}
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output {
let start = cmp::min(self.0.start, slice.len() - 1);
let end = cmp::min(self.0.end, slice.len() - 1);
// SAFETY: the caller ensures that the slice isn't empty
unsafe { (start..=end).get_unchecked_mut(slice) }
}
fn index(self, slice: &[T]) -> &Self::Output {
let start = cmp::min(self.0.start, slice.len() - 1);
let end = cmp::min(self.0.end, slice.len() - 1);
(start..=end).index(slice)
}
fn index_mut(self, slice: &mut [T]) -> &mut Self::Output {
let start = cmp::min(self.0.start, slice.len() - 1);
let end = cmp::min(self.0.end, slice.len() - 1);
(start..=end).index_mut(slice)
}
}
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
unsafe impl<T> SliceIndex<[T]> for Clamp<range::RangeFrom<usize>> {
type Output = [T];
fn get(self, slice: &[T]) -> Option<&Self::Output> {
(cmp::min(self.0.start, slice.len())..).get(slice)
}
fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> {
(cmp::min(self.0.start, slice.len())..).get_mut(slice)
}
unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output {
// SAFETY: a range starting at len is valid
unsafe { (cmp::min(self.0.start, slice.len())..).get_unchecked(slice) }
}
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output {
// SAFETY: a range starting at len is valid
unsafe { (cmp::min(self.0.start, slice.len())..).get_unchecked_mut(slice) }
}
fn index(self, slice: &[T]) -> &Self::Output {
(cmp::min(self.0.start, slice.len())..).index(slice)
}
fn index_mut(self, slice: &mut [T]) -> &mut Self::Output {
(cmp::min(self.0.start, slice.len())..).index_mut(slice)
}
}
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
unsafe impl<T> SliceIndex<[T]> for Clamp<ops::RangeFrom<usize>> {
type Output = [T];
fn get(self, slice: &[T]) -> Option<&Self::Output> {
(cmp::min(self.0.start, slice.len())..).get(slice)
}
fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> {
(cmp::min(self.0.start, slice.len())..).get_mut(slice)
}
unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output {
// SAFETY: a range starting at len is valid
unsafe { (cmp::min(self.0.start, slice.len())..).get_unchecked(slice) }
}
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output {
// SAFETY: a range starting at len is valid
unsafe { (cmp::min(self.0.start, slice.len())..).get_unchecked_mut(slice) }
}
fn index(self, slice: &[T]) -> &Self::Output {
(cmp::min(self.0.start, slice.len())..).index(slice)
}
fn index_mut(self, slice: &mut [T]) -> &mut Self::Output {
(cmp::min(self.0.start, slice.len())..).index_mut(slice)
}
}
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
unsafe impl<T> SliceIndex<[T]> for Clamp<range::RangeTo<usize>> {
type Output = [T];
fn get(self, slice: &[T]) -> Option<&Self::Output> {
(..cmp::min(self.0.end, slice.len())).get(slice)
}
fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> {
(..cmp::min(self.0.end, slice.len())).get_mut(slice)
}
unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output {
// SAFETY: a range ending before len is always valid
unsafe { (..cmp::min(self.0.end, slice.len())).get_unchecked(slice) }
}
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output {
// SAFETY: a range ending before len is always valid
unsafe { (..cmp::min(self.0.end, slice.len())).get_unchecked_mut(slice) }
}
fn index(self, slice: &[T]) -> &Self::Output {
(..cmp::min(self.0.end, slice.len())).index(slice)
}
fn index_mut(self, slice: &mut [T]) -> &mut Self::Output {
(..cmp::min(self.0.end, slice.len())).index_mut(slice)
}
}
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
unsafe impl<T> SliceIndex<[T]> for Clamp<range::RangeToInclusive<usize>> {
type Output = [T];
fn get(self, slice: &[T]) -> Option<&Self::Output> {
(..=cmp::min(self.0.last, slice.len() - 1)).get(slice)
}
fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> {
(..=cmp::min(self.0.last, slice.len() - 1)).get_mut(slice)
}
unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output {
// SAFETY: the caller ensures that the slice isn't empty
unsafe { (..=cmp::min(self.0.last, slice.len() - 1)).get_unchecked(slice) }
}
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output {
// SAFETY: the caller ensures that the slice isn't empty
unsafe { (..=cmp::min(self.0.last, slice.len() - 1)).get_unchecked_mut(slice) }
}
fn index(self, slice: &[T]) -> &Self::Output {
(..=cmp::min(self.0.last, slice.len() - 1)).index(slice)
}
fn index_mut(self, slice: &mut [T]) -> &mut Self::Output {
(..=cmp::min(self.0.last, slice.len() - 1)).index_mut(slice)
}
}
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
unsafe impl<T> SliceIndex<[T]> for Clamp<ops::RangeToInclusive<usize>> {
type Output = [T];
fn get(self, slice: &[T]) -> Option<&Self::Output> {
(..=cmp::min(self.0.end, slice.len() - 1)).get(slice)
}
fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> {
(..=cmp::min(self.0.end, slice.len() - 1)).get_mut(slice)
}
unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output {
// SAFETY: the caller ensures that the slice isn't empty
unsafe { (..=cmp::min(self.0.end, slice.len() - 1)).get_unchecked(slice) }
}
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output {
// SAFETY: the caller ensures that the slice isn't empty
unsafe { (..=cmp::min(self.0.end, slice.len() - 1)).get_unchecked_mut(slice) }
}
fn index(self, slice: &[T]) -> &Self::Output {
(..=cmp::min(self.0.end, slice.len() - 1)).index(slice)
}
fn index_mut(self, slice: &mut [T]) -> &mut Self::Output {
(..=cmp::min(self.0.end, slice.len() - 1)).index_mut(slice)
}
}
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
unsafe impl<T> SliceIndex<[T]> for Clamp<range::RangeFull> {
type Output = [T];
fn get(self, slice: &[T]) -> Option<&Self::Output> {
(..).get(slice)
}
fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> {
(..).get_mut(slice)
}
unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output {
// SAFETY: RangeFull just returns `slice` here
unsafe { (..).get_unchecked(slice) }
}
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output {
// SAFETY: RangeFull just returns `slice` here
unsafe { (..).get_unchecked_mut(slice) }
}
fn index(self, slice: &[T]) -> &Self::Output {
(..).index(slice)
}
fn index_mut(self, slice: &mut [T]) -> &mut Self::Output {
(..).index_mut(slice)
}
}
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
unsafe impl<T> SliceIndex<[T]> for Last {
type Output = T;
fn get(self, slice: &[T]) -> Option<&Self::Output> {
slice.last()
}
fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> {
slice.last_mut()
}
unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output {
// SAFETY: the caller ensures that the slice isn't empty
unsafe { slice_get_unchecked(slice, slice.len() - 1) }
}
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output {
// SAFETY: the caller ensures that the slice isn't empty
unsafe { slice_get_unchecked(slice, slice.len() - 1) }
}
fn index(self, slice: &[T]) -> &Self::Output {
// N.B., use intrinsic indexing
&(*slice)[slice.len() - 1]
}
fn index_mut(self, slice: &mut [T]) -> &mut Self::Output {
// N.B., use intrinsic indexing
&mut (*slice)[slice.len() - 1]
}
}

View file

@ -294,6 +294,7 @@ pub mod cmp;
pub mod convert;
pub mod default;
pub mod error;
pub mod index;
pub mod marker;
pub mod ops;

View file

@ -650,6 +650,18 @@ impl<Idx: PartialOrd<Idx>> RangeToInclusive<Idx> {
}
}
impl<T> From<legacy::RangeToInclusive<T>> for RangeToInclusive<T> {
fn from(value: legacy::RangeToInclusive<T>) -> Self {
Self { last: value.end }
}
}
impl<T> From<RangeToInclusive<T>> for legacy::RangeToInclusive<T> {
fn from(value: RangeToInclusive<T>) -> Self {
Self { end: value.last }
}
}
// RangeToInclusive<Idx> cannot impl From<RangeTo<Idx>>
// because underflow would be possible with (..0).into()

View file

@ -135,6 +135,11 @@ mod private_slice_index {
impl Sealed for range::RangeFrom<usize> {}
impl Sealed for ops::IndexRange {}
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
impl Sealed for crate::index::Last {}
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
impl<T> Sealed for crate::index::Clamp<T> where T: Sealed {}
}
/// A helper trait used for indexing operations.

View file

@ -0,0 +1,83 @@
use core::index::Clamp;
use core::range;
use core::slice::SliceIndex;
macro_rules! test_clamp {
($range:expr, $(($slice:expr, $other:expr)),+) => {
$(
assert_eq!(Clamp($range.clone()).get(&$slice as &[_]), $other.get(&$slice as &[_]));
assert_eq!(Clamp($range.clone()).get_mut(&mut $slice as &mut [_]), $other.get_mut(&mut $slice as &mut [_]));
unsafe {
assert_eq!(&*Clamp($range.clone()).get_unchecked(&$slice as &[_]), &*$other.get_unchecked(&$slice as &[_]));
assert_eq!(&*Clamp($range.clone()).get_unchecked_mut(&mut $slice as &mut [_]), &*$other.get_unchecked_mut(&mut $slice as &mut [_]));
}
assert_eq!(Clamp($range.clone()).index(&$slice as &[_]), $other.index(&$slice as &[_]));
assert_eq!(Clamp($range.clone()).index_mut(&mut $slice as &mut [_]), $other.index_mut(&mut $slice as &mut [_]));
)+
};
}
#[test]
fn test_clamp_usize() {
test_clamp!(2, ([0, 1], 1), ([0, 1, 2], 2));
}
#[test]
fn test_clamp_range_range() {
test_clamp!(range::Range::from(1..4), ([0, 1], 1..2), ([0, 1, 2, 3, 4], 1..4), ([0], 1..1));
}
#[test]
fn test_clamp_ops_range() {
test_clamp!(1..4, ([0, 1], 1..2), ([0, 1, 2, 3, 4], 1..4), ([0], 1..1));
}
#[test]
fn test_clamp_range_range_inclusive() {
test_clamp!(
range::RangeInclusive::from(1..=3),
([0, 1], 1..=1),
([0, 1, 2, 3, 4], 1..=3),
([0], 0..=0)
);
}
#[test]
fn test_clamp_ops_range_inclusive() {
test_clamp!(1..=3, ([0, 1], 1..=1), ([0, 1, 2, 3, 4], 1..=3), ([0], 0..=0));
}
#[test]
fn test_clamp_range_range_from() {
test_clamp!(range::RangeFrom::from(1..), ([0, 1], 1..), ([0, 1, 2, 3, 4], 1..), ([0], 1..));
}
#[test]
fn test_clamp_ops_range_from() {
test_clamp!(1.., ([0, 1], 1..), ([0, 1, 2, 3, 4], 1..), ([0], 1..));
}
#[test]
fn test_clamp_range_to() {
test_clamp!(..4, ([0, 1], ..2), ([0, 1, 2, 3, 4], ..4), ([0], ..1));
}
#[test]
fn test_clamp_range_range_to_inclusive() {
test_clamp!(
range::RangeToInclusive::from(..=4),
([0, 1], ..=1),
([0, 1, 2, 3, 4], ..=4),
([0], ..=0)
);
}
#[test]
fn test_clamp_ops_range_to_inclusive() {
test_clamp!(..=4, ([0, 1], ..=1), ([0, 1, 2, 3, 4], ..=4), ([0], ..=0));
}
#[test]
fn test_clamp_range_full() {
test_clamp!(.., ([0, 1], ..), ([0, 1, 2, 3, 4], ..), ([0], ..));
}

View file

@ -85,6 +85,7 @@
#![feature(maybe_uninit_write_slice)]
#![feature(min_specialization)]
#![feature(never_type)]
#![feature(new_range_api)]
#![feature(next_index)]
#![feature(non_exhaustive_omitted_patterns_lint)]
#![feature(numfmt)]
@ -97,9 +98,11 @@
#![feature(ptr_metadata)]
#![feature(result_option_map_or_default)]
#![feature(slice_from_ptr_range)]
#![feature(slice_index_methods)]
#![feature(slice_internals)]
#![feature(slice_partition_dedup)]
#![feature(slice_split_once)]
#![feature(sliceindex_wrappers)]
#![feature(split_array)]
#![feature(split_as_slice)]
#![feature(std_internals)]
@ -178,6 +181,7 @@ mod fmt;
mod future;
mod hash;
mod hint;
mod index;
mod intrinsics;
mod io;
mod iter;