-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add slice::check_range
#75207
Add slice::check_range
#75207
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,7 +30,7 @@ use crate::intrinsics::{assume, exact_div, is_aligned_and_not_null, unchecked_su | |
use crate::iter::*; | ||
use crate::marker::{self, Copy, Send, Sized, Sync}; | ||
use crate::mem; | ||
use crate::ops::{self, FnMut, Range}; | ||
use crate::ops::{self, Bound, FnMut, Range, RangeBounds}; | ||
use crate::option::Option; | ||
use crate::option::Option::{None, Some}; | ||
use crate::ptr::{self, NonNull}; | ||
|
@@ -350,6 +350,79 @@ impl<T> [T] { | |
unsafe { &mut *index.get_unchecked_mut(self) } | ||
} | ||
|
||
/// Converts a range over this slice to [`Range`]. | ||
/// | ||
/// The returned range is safe to pass to [`get_unchecked`] and [`get_unchecked_mut`]. | ||
/// | ||
/// [`get_unchecked`]: #method.get_unchecked | ||
/// [`get_unchecked_mut`]: #method.get_unchecked_mut | ||
/// | ||
/// # Panics | ||
/// | ||
/// Panics if the range is out of bounds. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// #![feature(slice_check_range)] | ||
/// | ||
/// let v = [10, 40, 30]; | ||
/// assert_eq!(1..2, v.check_range(1..2)); | ||
/// assert_eq!(0..2, v.check_range(..2)); | ||
/// assert_eq!(1..3, v.check_range(1..)); | ||
/// ``` | ||
/// | ||
/// Panics when [`Index::index`] would panic: | ||
/// | ||
/// ```should_panic | ||
/// #![feature(slice_check_range)] | ||
/// | ||
/// [10, 40, 30].check_range(2..1); | ||
/// ``` | ||
/// | ||
/// ```should_panic | ||
/// #![feature(slice_check_range)] | ||
/// | ||
/// [10, 40, 30].check_range(1..4); | ||
/// ``` | ||
/// | ||
/// ```should_panic | ||
/// #![feature(slice_check_range)] | ||
/// | ||
/// [10, 40, 30].check_range(1..=usize::MAX); | ||
/// ``` | ||
/// | ||
/// [`Index::index`]: ops::Index::index | ||
#[track_caller] | ||
#[unstable(feature = "slice_check_range", issue = "none")] | ||
pub fn check_range<R: RangeBounds<usize>>(&self, range: R) -> Range<usize> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given that we only need the length of the slice, I don't think this method should take a reference to the full slice. References are very strong types, they assert the aliasing rules and they assert that the memory they point to is initialized. As my other messages here show, that is a problem for this method. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For future readers, this conversation was continued here. |
||
let start = match range.start_bound() { | ||
Bound::Included(&start) => start, | ||
Bound::Excluded(start) => { | ||
start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail()) | ||
} | ||
Bound::Unbounded => 0, | ||
}; | ||
|
||
let len = self.len(); | ||
let end = match range.end_bound() { | ||
Bound::Included(end) => { | ||
end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail()) | ||
} | ||
Bound::Excluded(&end) => end, | ||
Bound::Unbounded => len, | ||
}; | ||
|
||
if start > end { | ||
slice_index_order_fail(start, end); | ||
} | ||
if end > len { | ||
slice_end_index_len_fail(end, len); | ||
} | ||
|
||
Range { start, end } | ||
} | ||
|
||
/// Returns a raw pointer to the slice's buffer. | ||
/// | ||
/// The caller must ensure that the slice outlives the pointer this | ||
|
@@ -2445,13 +2518,13 @@ impl<T> [T] { | |
let src_start = match src.start_bound() { | ||
ops::Bound::Included(&n) => n, | ||
ops::Bound::Excluded(&n) => { | ||
n.checked_add(1).unwrap_or_else(|| slice_index_overflow_fail()) | ||
n.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail()) | ||
} | ||
ops::Bound::Unbounded => 0, | ||
}; | ||
let src_end = match src.end_bound() { | ||
ops::Bound::Included(&n) => { | ||
n.checked_add(1).unwrap_or_else(|| slice_index_overflow_fail()) | ||
n.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail()) | ||
} | ||
ops::Bound::Excluded(&n) => n, | ||
ops::Bound::Unbounded => self.len(), | ||
|
@@ -3034,7 +3107,14 @@ fn slice_index_order_fail(index: usize, end: usize) -> ! { | |
#[inline(never)] | ||
#[cold] | ||
#[track_caller] | ||
fn slice_index_overflow_fail() -> ! { | ||
fn slice_start_index_overflow_fail() -> ! { | ||
panic!("attempted to index slice from after maximum usize"); | ||
} | ||
|
||
#[inline(never)] | ||
#[cold] | ||
#[track_caller] | ||
fn slice_end_index_overflow_fail() -> ! { | ||
panic!("attempted to index slice up to maximum usize"); | ||
} | ||
|
||
|
@@ -3370,15 +3450,15 @@ unsafe impl<T> SliceIndex<[T]> for ops::RangeInclusive<usize> { | |
#[inline] | ||
fn index(self, slice: &[T]) -> &[T] { | ||
if *self.end() == usize::MAX { | ||
slice_index_overflow_fail(); | ||
slice_end_index_overflow_fail(); | ||
} | ||
(*self.start()..self.end() + 1).index(slice) | ||
} | ||
|
||
#[inline] | ||
fn index_mut(self, slice: &mut [T]) -> &mut [T] { | ||
if *self.end() == usize::MAX { | ||
slice_index_overflow_fail(); | ||
slice_end_index_overflow_fail(); | ||
} | ||
(*self.start()..self.end() + 1).index_mut(slice) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't unfortunately return a
CheckedRange<usize>
because there's no association with a specific range, so it's useless.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain more? I don't see how this would be useless, since it converts
RangeBounds
tousize
s. It wasn't meant to reduce unsafe code.It could return a
CheckedRange
that has a reference to the slice and a safeget
method. However, that would requirecheck_range_mut
andCheckedRangeMut
too, which would make this simple method much more complex.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your code is useful. I meant to say that returning a CheckedRange is not so useful. As you say it could be done with the reference to the slice, but it could make it over engineered, so all is OK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, that makes sense. Thanks for the clarification.