diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 72af47c71dd64..8b7da1456970f 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -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; + fn slice_rposition(&self, x: &[Self]) -> Option; +} + +impl SlicePosition for T +where + T: PartialEq, +{ + default fn slice_position(&self, x: &[Self]) -> Option { + x.iter().position(|y| *y == *self) + } + default fn slice_rposition(&self, x: &[Self]) -> Option { + x.iter().rposition(|y| *y == *self) + } +} + +impl SlicePosition for u8 { + #[inline] + fn slice_position(&self, x: &[Self]) -> Option { + memchr::memchr(*self, x) + } + #[inline] + fn slice_rposition(&self, x: &[Self]) -> Option { + memchr::memrchr(*self, x) + } +} + +impl SlicePosition for i8 { + #[inline] + fn slice_position(&self, x: &[Self]) -> Option { + memchr::memchr(*self as u8, i8s_to_u8s(x)) + } + #[inline] + fn slice_rposition(&self, x: &[Self]) -> Option { + 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()) } +} diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index ec28cdd1ba0d9..1033b3c987b33 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -1965,6 +1965,69 @@ impl [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` + /// (e.g. `String: Borrow`), 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 + 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` + /// (e.g. `String: Borrow`), 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 + where + T: PartialEq, + { + cmp::SlicePosition::slice_rposition(x, self) + } + /// Returns `true` if `needle` is a prefix of the slice. /// /// # Examples diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 7dc6e220c08bc..3a1ea6a89c65e 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -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)] diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index 7e198631cc7eb..4bbff538a737c 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -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