Skip to content

Add unstable <[T]>::{position, rposition} functions #84058

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
56 changes: 49 additions & 7 deletions library/core/src/slice/cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,12 +250,54 @@ impl SliceContains for u8 {
impl SliceContains for i8 {
#[inline]
fn slice_contains(&self, x: &[Self]) -> bool {
let byte = *self as u8;
// SAFETY: `i8` and `u8` have the same memory layout, thus casting `x.as_ptr()`
// as `*const u8` is safe. The `x.as_ptr()` comes from a reference and is thus guaranteed
// to be valid for reads for the length of the slice `x.len()`, which cannot be larger
// than `isize::MAX`. The returned slice is never mutated.
let bytes: &[u8] = unsafe { from_raw_parts(x.as_ptr() as *const u8, x.len()) };
memchr::memchr(byte, bytes).is_some()
memchr::memchr(*self as u8, i8s_to_u8s(x)).is_some()
}
}

pub(super) trait SlicePosition: Sized {
fn slice_position(&self, x: &[Self]) -> Option<usize>;
fn slice_rposition(&self, x: &[Self]) -> Option<usize>;
}

impl<T> SlicePosition for T
where
T: PartialEq,
{
default fn slice_position(&self, x: &[Self]) -> Option<usize> {
x.iter().position(|y| *y == *self)
}
default fn slice_rposition(&self, x: &[Self]) -> Option<usize> {
x.iter().rposition(|y| *y == *self)
}
}

impl SlicePosition for u8 {
#[inline]
fn slice_position(&self, x: &[Self]) -> Option<usize> {
memchr::memchr(*self, x)
}
#[inline]
fn slice_rposition(&self, x: &[Self]) -> Option<usize> {
memchr::memrchr(*self, x)
}
}

impl SlicePosition for i8 {
#[inline]
fn slice_position(&self, x: &[Self]) -> Option<usize> {
memchr::memchr(*self as u8, i8s_to_u8s(x))
}
#[inline]
fn slice_rposition(&self, x: &[Self]) -> Option<usize> {
memchr::memrchr(*self as u8, i8s_to_u8s(x))
}
}

#[inline]
fn i8s_to_u8s(s: &[i8]) -> &[u8] {
// SAFETY: `i8` and `u8` have the same memory layout, thus casting `x.as_ptr()`
// as `*const u8` is safe. The `x.as_ptr()` comes from a reference and is thus guaranteed
// to be valid for reads for the length of the slice `x.len()`, which cannot be larger
// than `isize::MAX`. The returned slice is never mutated.
unsafe { from_raw_parts(s.as_ptr() as *const u8, s.len()) }
}
63 changes: 63 additions & 0 deletions library/core/src/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1965,6 +1965,69 @@ impl<T> [T] {
cmp::SliceContains::slice_contains(x, self)
}

/// If the slice contains at least one element with the given
/// value, returns the first index of that value. Otherwise,
/// returns `None`.
///
/// # Examples
///
/// ```
/// #![feature(slice_position)]
/// let v = [10, 40, 30, 20, 30];
/// assert_eq!(v.position(&30), Some(2));
/// assert!(v.position(&50).is_none());
/// ```
///
/// If you do not have an `&T`, but just an `&U` such that `T: Borrow<U>`
/// (e.g. `String: Borrow<str>`), you can use `iter().position`:
///
/// ```
/// // slice of `String`
/// let v = [String::from("hello"), String::from("world")];
/// // search with `&str`
/// assert_eq!(v.iter().position(|e| e == "hello"), Some(0));
/// assert_eq!(v.iter().position(|e| e == "hi"), None);
/// ```
#[unstable(feature = "slice_position", issue = "none", reason = "recently added")]
#[inline]
pub fn position(&self, x: &T) -> Option<usize>
where
T: PartialEq,
{
cmp::SlicePosition::slice_position(x, self)
}

/// If the slice contains at element with the given value, returns
/// the last index of that value. Otherwise, returns `None`.
///
/// # Examples
///
/// ```
/// #![feature(slice_position)]
/// let v = [10, 40, 30, 20, 30];
/// assert_eq!(v.rposition(&30), Some(4));
/// assert!(v.rposition(&50).is_none());
/// ```
///
/// If you do not have an `&T`, but just an `&U` such that `T: Borrow<U>`
/// (e.g. `String: Borrow<str>`), you can use `iter().rposition`:
///
/// ```
/// // slice of `String`
/// let v = [String::from("hello"), String::from("world")];
/// // search with `&str`
/// assert_eq!(v.iter().rposition(|e| e == "hello"), Some(0));
/// assert_eq!(v.iter().rposition(|e| e == "hi"), None);
/// ```
#[unstable(feature = "slice_position", issue = "none", reason = "recently added")]
#[inline]
pub fn rposition(&self, x: &T) -> Option<usize>
where
T: PartialEq,
{
cmp::SlicePosition::slice_rposition(x, self)
}

/// Returns `true` if `needle` is a prefix of the slice.
///
/// # Examples
Expand Down
1 change: 1 addition & 0 deletions library/core/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
#![feature(const_option)]
#![feature(integer_atomics)]
#![feature(slice_group_by)]
#![feature(slice_position)]
#![feature(trusted_random_access)]
#![feature(unsize)]
#![deny(unsafe_op_in_unsafe_fn)]
Expand Down
31 changes: 31 additions & 0 deletions library/core/tests/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2025,6 +2025,37 @@ fn test_is_sorted() {
assert!(["c", "bb", "aaa"].is_sorted_by_key(|s| s.len()));
}

#[test]
fn test_iter_position() {
// unspecialized
assert_eq!([1u32, 2, 2, 9].position(&2), Some(1));
assert_eq!([1u32, 2, 2, 9].rposition(&2), Some(2));
assert_eq!([1u32, 2, 2, 9].position(&1), Some(0));
assert_eq!([1u32, 2, 2, 9].rposition(&1), Some(0));
assert_eq!([1u32, 2, 2, 9].position(&9), Some(3));
assert_eq!([1u32, 2, 2, 9].rposition(&9), Some(3));
assert_eq!([1u32, 2, 2, 9].rposition(&6), None);
assert_eq!([1u32, 2, 2, 9].position(&6), None);
// specialized
assert_eq!([1u8, 2, 2, 9].position(&2), Some(1));
assert_eq!([1u8, 2, 2, 9].rposition(&2), Some(2));
assert_eq!([1u8, 2, 2, 9].position(&1), Some(0));
assert_eq!([1u8, 2, 2, 9].rposition(&1), Some(0));
assert_eq!([1u8, 2, 2, 9].position(&9), Some(3));
assert_eq!([1u8, 2, 2, 9].rposition(&9), Some(3));
assert_eq!([1u8, 2, 2, 9].rposition(&6), None);
assert_eq!([1u8, 2, 2, 9].position(&6), None);
// also specilaized
assert_eq!([1i8, 2, 2, 9].position(&2), Some(1));
assert_eq!([1i8, 2, 2, 9].rposition(&2), Some(2));
assert_eq!([1i8, 2, 2, 9].position(&1), Some(0));
assert_eq!([1i8, 2, 2, 9].rposition(&1), Some(0));
assert_eq!([1i8, 2, 2, 9].position(&9), Some(3));
assert_eq!([1i8, 2, 2, 9].rposition(&9), Some(3));
assert_eq!([1i8, 2, 2, 9].rposition(&6), None);
assert_eq!([1i8, 2, 2, 9].position(&6), None);
}

#[test]
fn test_slice_run_destructors() {
// Make sure that destructors get run on slice literals
Expand Down