Skip to content
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

Merged
merged 5 commits into from
Sep 4, 2020
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 87 additions & 6 deletions library/core/src/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -350,6 +350,80 @@ 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
/// [`Range`]: ../ops/struct.Range.html
///
/// # 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/trait.Index.html#tymethod.index
#[track_caller]
#[unstable(feature = "slice_check_range", issue = "none")]
pub fn check_range<R: RangeBounds<usize>>(&self, range: R) -> Range<usize> {
Copy link

@leonardo-m leonardo-m Aug 6, 2020

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.

Copy link
Contributor Author

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 to usizes. It wasn't meant to reduce unsafe code.

It could return a CheckedRange that has a reference to the slice and a safe get method. However, that would require check_range_mut and CheckedRangeMut too, which would make this simple method much more complex.

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.

Copy link
Contributor Author

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.

Copy link
Member

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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
Expand Down Expand Up @@ -2445,13 +2519,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(),
Expand Down Expand Up @@ -3034,7 +3108,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");
}

Expand Down Expand Up @@ -3370,15 +3451,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)
}
Expand Down