Skip to content

Add range_of to slice/str return a Range, opposite to get #96612

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
49 changes: 49 additions & 0 deletions library/core/src/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4018,6 +4018,55 @@ impl<T> [T] {
*self = rem;
Some(last)
}

/// Gives the `Range` where the `other` slice is located within `self`,
/// or `None` if it is not a sub-slice.
///
/// This method can be thought of as the opposite of [`get`](#method.get),
/// and they mutually guarantee slice-to-slice roundtrips, as well as
/// range-to-range roundtrips if `T` is not a Zero-Sized Type.
///
/// This means that the resulting [`Range`] can be passed to
/// [`get`](#method.get) and will always return a slice that is [`ptr::eq`]
/// to the one passed to this function.
///
/// If `T` is a Zero-Sized Type ("ZST"), the resulting [`Range`] will always
/// start at `0`, and thus range-to-range roundtrips are not guaranteed.
///
/// # Examples
///
/// ```
/// #![feature(slice_range_of)]
///
/// let slice: &[u8] = &[1, 2, 3, 4, 5];
/// let subslice = &slice[1..3];
/// let foreign_slice: &[u8] = &[2, 3];
///
/// assert_eq!(slice.range_of(subslice), Some(1..3));
/// assert_eq!(slice.range_of(foreign_slice), None);
/// ```
#[unstable(feature = "slice_range_of", issue = "none")]
#[inline]
pub fn range_of(&self, other: &Self) -> Option<Range<usize>> {
if mem::size_of::<T>() == 0 {
return if self.as_ptr() == other.as_ptr() && self.len() <= other.len() {
Some(0..self.len())
} else {
None
};
}

let self_ptr = self.as_ptr_range();
let other_ptr = other.as_ptr_range();
if self_ptr.start <= other_ptr.start && other_ptr.end <= self_ptr.end {
// SAFETY: the bounds checks above uphold the safety contract for `sub_ptr`.
Some(unsafe {
other_ptr.start.sub_ptr(self_ptr.start)..other_ptr.end.sub_ptr(self_ptr.start)
})
} else {
None
}
}
}

impl<T, const N: usize> [[T; N]] {
Expand Down
34 changes: 34 additions & 0 deletions library/core/src/str/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use self::pattern::{DoubleEndedSearcher, ReverseSearcher, Searcher};

use crate::char::{self, EscapeDebugExtArgs};
use crate::mem;
use crate::ops::Range;
use crate::slice::{self, SliceIndex};

pub mod pattern;
Expand Down Expand Up @@ -2555,6 +2556,39 @@ impl str {
pub fn escape_unicode(&self) -> EscapeUnicode<'_> {
EscapeUnicode { inner: self.chars().flat_map(CharEscapeUnicode) }
}

/// Gives the `Range` where `substr` is located within `self`, or `None`
/// if it is not a sub-string.
///
/// This method can be thought of as the opposite of [`get`](#method.get),
/// and they mutually guarantee slice-to-slice roundtrips and
/// range-to-range roundtrips.
///
/// This means that the resulting [`Range`] can be passed to
/// [`get`](#method.get) and will always return a sub-string that is
/// [`ptr::eq`](std::ptr::eq) to the one passed to this function.
///
/// This is different from using [`find`](#method.find) as it does not do
/// a search, but rather a pointer comparison. The given `substr` must
/// point to the same memory location as `self`.
///
/// # Examples
///
/// ```
/// #![feature(slice_range_of)]
///
/// let containing_str = "abcdefg";
/// let other_str = "abcdef.h";
/// let substr = &containing_str[1..3];
///
/// assert_eq!(containing_str.range_of(substr), Some(1..3));
/// assert_eq!(other_str.range_of(substr), None);
/// ```
#[unstable(feature = "slice_range_of", issue = "none")]
#[inline]
pub fn range_of(&self, substr: &Self) -> Option<Range<usize>> {
self.as_bytes().range_of(substr.as_bytes())
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down