diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 4c86d7e06ae44..6e36d407b442c 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -10,6 +10,7 @@ use core::borrow::{Borrow, BorrowMut}; use core::iter::FusedIterator; use core::mem::MaybeUninit; +use core::pattern::{Pattern, Utf8Pattern}; #[stable(feature = "encode_utf16", since = "1.8.0")] pub use core::str::EncodeUtf16; #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] @@ -20,7 +21,6 @@ pub use core::str::SplitInclusive; pub use core::str::SplitWhitespace; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::pattern; -use core::str::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher, Utf8Pattern}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::{Bytes, CharIndices, Chars, from_utf8, from_utf8_mut}; #[stable(feature = "str_escape", since = "1.34.0")] @@ -305,7 +305,10 @@ impl str { without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn replace(&self, from: P, to: &str) -> String { + pub fn replace

(&self, from: P, to: &str) -> String + where + P: Pattern, + { // Fast path for replacing a single ASCII character with another. if let Some(from_byte) = match from.as_utf8_pattern() { Some(Utf8Pattern::StringPattern(s)) => match s.as_bytes() { @@ -363,7 +366,10 @@ impl str { #[must_use = "this returns the replaced string as a new allocation, \ without modifying the original"] #[stable(feature = "str_replacen", since = "1.16.0")] - pub fn replacen(&self, pat: P, to: &str, count: usize) -> String { + pub fn replacen

(&self, pat: P, to: &str, count: usize) -> String + where + P: Pattern, + { // Hope to reduce the times of re-allocation let mut result = String::with_capacity(32); let mut last_end = 0; diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index b3fb35a086dcd..bfe93c94b3f33 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -53,7 +53,7 @@ use core::ops::Add; #[cfg(not(no_global_oom_handling))] use core::ops::AddAssign; use core::ops::{self, Range, RangeBounds}; -use core::str::pattern::{Pattern, Utf8Pattern}; +use core::pattern::{Pattern, Utf8Pattern}; use core::{fmt, hash, ptr, slice}; #[cfg(not(no_global_oom_handling))] @@ -1593,8 +1593,11 @@ impl String { /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "string_remove_matches", issue = "72826")] - pub fn remove_matches(&mut self, pat: P) { - use core::str::pattern::Searcher; + pub fn remove_matches<'a, P>(&'a mut self, pat: P) + where + P: Pattern, + { + use core::pattern::Searcher; let rejections = { let mut searcher = pat.into_searcher(self); @@ -2143,7 +2146,10 @@ impl String { /// [replacen]: ../../std/primitive.str.html#method.replacen #[cfg(not(no_global_oom_handling))] #[unstable(feature = "string_replace_in_place", issue = "147949")] - pub fn replace_first(&mut self, from: P, to: &str) { + pub fn replace_first<'a, P>(&'a mut self, from: P, to: &str) + where + P: Pattern, + { let range = match self.match_indices(from).next() { Some((start, match_str)) => start..start + match_str.len(), None => return, @@ -2169,9 +2175,10 @@ impl String { /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "string_replace_in_place", issue = "147949")] - pub fn replace_last(&mut self, from: P, to: &str) + pub fn replace_last<'a, P>(&'a mut self, from: P, to: &str) where - for<'a> P::Searcher<'a>: core::str::pattern::ReverseSearcher<'a>, + P: Pattern, + for<'x> P::Searcher<'x>: core::pattern::ReverseSearcher<'x, str>, { let range = match self.rmatch_indices(from).next() { Some((start, match_str)) => start..start + match_str.len(), @@ -2664,10 +2671,10 @@ impl<'a> Extend<&'a core::ascii::Char> for String { reason = "API not fully fleshed out and ready to be stabilized", issue = "27721" )] -impl<'b> Pattern for &'b String { - type Searcher<'a> = <&'b str as Pattern>::Searcher<'a>; +impl<'b> Pattern for &'b String { + type Searcher<'h> = <&'b str as Pattern>::Searcher<'h>; - fn into_searcher(self, haystack: &str) -> <&'b str as Pattern>::Searcher<'_> { + fn into_searcher<'h>(self, haystack: &'h str) -> <&'b str as Pattern>::Searcher<'h> { self[..].into_searcher(haystack) } @@ -2682,22 +2689,22 @@ impl<'b> Pattern for &'b String { } #[inline] - fn strip_prefix_of(self, haystack: &str) -> Option<&str> { + fn strip_prefix_of<'h>(self, haystack: &'h str) -> Option<&'h str> { self[..].strip_prefix_of(haystack) } #[inline] - fn is_suffix_of<'a>(self, haystack: &'a str) -> bool + fn is_suffix_of<'h>(self, haystack: &'h str) -> bool where - Self::Searcher<'a>: core::str::pattern::ReverseSearcher<'a>, + Self::Searcher<'h>: core::pattern::ReverseSearcher<'h, str>, { self[..].is_suffix_of(haystack) } #[inline] - fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&'a str> + fn strip_suffix_of<'h>(self, haystack: &'h str) -> Option<&'h str> where - Self::Searcher<'a>: core::str::pattern::ReverseSearcher<'a>, + Self::Searcher<'h>: core::pattern::ReverseSearcher<'h, str>, { self[..].strip_suffix_of(haystack) } diff --git a/library/alloctests/tests/str.rs b/library/alloctests/tests/str.rs index 830f6972f5af5..f647bb1a39d43 100644 --- a/library/alloctests/tests/str.rs +++ b/library/alloctests/tests/str.rs @@ -2009,14 +2009,14 @@ fn test_repeat() { } mod pattern { - use std::str::pattern::SearchStep::{self, Done, Match, Reject}; - use std::str::pattern::{Pattern, ReverseSearcher, Searcher}; + use std::pattern::SearchStep::{self, Done, Match, Reject}; + use std::pattern::{Pattern, ReverseSearcher, Searcher}; macro_rules! make_test { ($name:ident, $p:expr, $h:expr, [$($e:expr,)*]) => { #[allow(unused_imports)] mod $name { - use std::str::pattern::SearchStep::{Match, Reject}; + use std::pattern::SearchStep::{Match, Reject}; use super::{cmp_search_to_vec}; #[test] fn fwd() { @@ -2032,7 +2032,8 @@ mod pattern { fn cmp_search_to_vec

(rev: bool, pat: P, haystack: &str, right: Vec) where - P: for<'a> Pattern: ReverseSearcher<'a>>, + P: Pattern, + for<'x> P::Searcher<'x>: ReverseSearcher<'x, str>, { let mut searcher = pat.into_searcher(haystack); let mut v = vec![]; @@ -2290,11 +2291,11 @@ generate_iterator_test! { #[test] fn different_str_pattern_forwarding_lifetimes() { - use std::str::pattern::Pattern; + use std::pattern::Pattern; fn foo

(p: P) where - for<'b> &'b P: Pattern, + for<'b> &'b P: Pattern, { for _ in 0..3 { "asdf".find(&p); diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index f026434acbbc1..c5bf8bea6accc 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -318,6 +318,7 @@ pub mod unsafe_binder; pub mod fmt; pub mod hash; +pub mod pattern; pub mod slice; pub mod str; pub mod time; diff --git a/library/core/src/pattern.rs b/library/core/src/pattern.rs new file mode 100644 index 0000000000000..b07a3c44f5d16 --- /dev/null +++ b/library/core/src/pattern.rs @@ -0,0 +1,403 @@ +//! The Pattern API. +//! +//! The Pattern API provides a generic mechanism for using different pattern +//! types when searching through different objects. +//! +//! For more details, see the traits [`Pattern`], [`Haystack`], [`Searcher`], +//! [`ReverseSearcher`] and [`DoubleEndedSearcher`]. Although this API is +//! unstable, it is exposed via stable methods on corresponding haystack types. +//! +//! # Examples +//! +//! [`Pattern`] is [implemented][pattern-impls] in the stable API for +//! [`&str`][`str`], [`char`], slices of [`char`], and functions and closures +//! implementing `FnMut(char) -> bool`. +//! +//! ``` +//! let s = "Can you find a needle in a haystack?"; +//! +//! // &str pattern +//! assert_eq!(s.find("you"), Some(4)); +//! // char pattern +//! assert_eq!(s.find('n'), Some(2)); +//! // array of chars pattern +//! assert_eq!(s.find(&['a', 'e', 'i', 'o', 'u']), Some(1)); +//! // slice of chars pattern +//! assert_eq!(s.find(&['a', 'e', 'i', 'o', 'u'][..]), Some(1)); +//! // closure pattern +//! assert_eq!(s.find(|c: char| c.is_ascii_punctuation()), Some(35)); +//! ``` +//! +//! [pattern-impls]: Pattern#implementors + +#![unstable( + feature = "pattern", + reason = "API not fully fleshed out and ready to be stabilized", + issue = "27721" +)] + +use crate::ops::Range; + +// Pattern + +/// A pattern which can be matched against a [`Haystack`]. +/// +/// A `Pattern` expresses that the implementing type can be used as +/// a pattern for searching in an `H`. For example, both the character `'a'` +/// and the string `"aa"` are patterns that would match at index `1` in +/// the string `"baaaab"`. +/// +/// The trait itself acts as a builder for an associated [`Searcher`] type, +/// which does the actual work of finding occurrences of the pattern in a haystack. +/// +/// Depending on the type of the pattern, the behavior of methods like +/// [`str::find`] and [`str::contains`] can change. The table below describes +/// some of those behaviors. +/// +/// | Pattern type | Match condition | +/// |--------------------------|-------------------------------------------| +/// | `&str` | is substring | +/// | `char` | is contained in string | +/// | `&[char]` | any char in slice is contained in string | +/// | `F: FnMut(char) -> bool` | `F` returns `true` for a char in string | +/// | `&&str` | is substring | +/// | `&String` | is substring | +/// +/// # Examples +/// +/// ``` +/// // &str +/// assert_eq!("abaaa".find("ba"), Some(1)); +/// assert_eq!("abaaa".find("bac"), None); +/// +/// // char +/// assert_eq!("abaaa".find('a'), Some(0)); +/// assert_eq!("abaaa".find('b'), Some(1)); +/// assert_eq!("abaaa".find('c'), None); +/// +/// // &[char; N] +/// assert_eq!("ab".find(&['b', 'a']), Some(0)); +/// assert_eq!("abaaa".find(&['a', 'z']), Some(0)); +/// assert_eq!("abaaa".find(&['c', 'd']), None); +/// +/// // &[char] +/// assert_eq!("ab".find(&['b', 'a'][..]), Some(0)); +/// assert_eq!("abaaa".find(&['a', 'z'][..]), Some(0)); +/// assert_eq!("abaaa".find(&['c', 'd'][..]), None); +/// +/// // FnMut(char) -> bool +/// assert_eq!("abcdef_z".find(|ch| ch > 'd' && ch < 'y'), Some(4)); +/// assert_eq!("abcddd_z".find(|ch| ch > 'd' && ch < 'y'), None); +/// ``` +pub trait Pattern: Sized { + /// Associated searcher for this pattern + type Searcher<'h>: Searcher<'h, H> + where + H: 'h; + + /// Constructs the associated searcher from + /// `self` and the `haystack` to search in. + fn into_searcher<'h>(self, haystack: &'h H) -> Self::Searcher<'h>; + + /// Checks whether the pattern matches anywhere in the haystack + #[inline] + fn is_contained_in(self, haystack: &H) -> bool { + self.into_searcher(haystack).next_match().is_some() + } + + /// Checks whether the pattern matches at the front of the haystack + #[inline] + fn is_prefix_of(self, haystack: &H) -> bool { + matches!( + self.into_searcher(haystack).next(), + SearchStep::Match(start, _) if start == haystack.cursor_at_front() + ) + } + + /// Checks whether the pattern matches at the back of the haystack + #[inline] + fn is_suffix_of<'h>(self, haystack: &'h H) -> bool + where + Self::Searcher<'h>: ReverseSearcher<'h, H>, + { + matches!( + self.into_searcher(haystack).next_back(), + SearchStep::Match(_, end) if end == haystack.cursor_at_back() + ) + } + + /// Removes the pattern from the front of a haystack, if it matches + #[inline] + fn strip_prefix_of<'h>(self, haystack: &'h H) -> Option<&'h H> { + if let SearchStep::Match(start, pos) = self.into_searcher(haystack).next() { + debug_assert_eq!( + start, + haystack.cursor_at_front(), + "The first search step from Searcher \ + must include the first character" + ); + let end = haystack.cursor_at_back(); + // SAFETY: `Searcher` is known to return valid indices. + Some(unsafe { haystack.get_unchecked(pos..end) }) + } else { + None + } + } + + /// Removes the pattern from the back of a haystack, if it matches. + #[inline] + fn strip_suffix_of<'h>(self, haystack: &'h H) -> Option<&'h H> + where + Self::Searcher<'h>: ReverseSearcher<'h, H>, + { + if let SearchStep::Match(pos, end) = self.into_searcher(haystack).next_back() { + debug_assert_eq!( + end, + haystack.cursor_at_back(), + "The first search step from ReverseSearcher \ + must include the last character" + ); + let start = haystack.cursor_at_front(); + // SAFETY: `Searcher` is known to return valid indices. + Some(unsafe { haystack.get_unchecked(start..pos) }) + } else { + None + } + } + + /// Returns the pattern as UTF-8 if possible. + fn as_utf8_pattern(&self) -> Option> { + None + } +} +/// Result of calling [`Pattern::as_utf8_pattern()`]. +/// Can be used for inspecting the contents of a [`Pattern`] in cases +/// where the underlying representation can be represented as UTF-8. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum Utf8Pattern<'a> { + /// Type returned by String and str types. + /// This stores `str` rather than bytes so callers cannot describe + /// non-UTF-8 string patterns through this API. + StringPattern(&'a str), + /// Type returned by char types. + CharPattern(char), +} + +// Haystack + +/// A type which can be searched in using a [`Pattern`]. +/// +/// The trait is used in combination with the [`Pattern`] trait to express a pattern +/// that can be used to search for elements in a given haystack. +pub trait Haystack { + /// Returns a cursor pointing at the beginning of the haystack. + fn cursor_at_front(&self) -> usize; + + /// Returns a cursor pointing at the end of the haystack. + fn cursor_at_back(&self) -> usize; + + /// Returns whether the haystack is empty. + fn is_empty(&self) -> bool { + self.cursor_at_front() == self.cursor_at_back() + } + + /// Returns portions of the haystack indicated by the cursor range. + /// + /// # Safety + /// + /// The range's start and end must be valid haystack split positions, + /// and start must not point to a position after end. + /// + /// Valid split positions are: + /// - the front of the haystack (as returned by + /// [`cursor_at_front()`][Self::cursor_at_front]), + /// - the back of the haystack (as returned by + /// [`cursor_at_back()`][Self::cursor_at_back]), or + /// - any cursor returned by a [`Searcher`] or [`ReverseSearcher`]. + unsafe fn get_unchecked(&self, range: Range) -> &Self; +} + +// Searcher + +/// Result of calling [`Searcher::next()`] or [`ReverseSearcher::next_back()`]. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum SearchStep { + /// Expresses that a match of the pattern has been found at + /// `haystack[a..b]`. + Match(usize, usize), + /// Expresses that `haystack[a..b]` has been rejected as a possible match + /// of the pattern. + /// + /// Note that there might be more than one `Reject` between two `Match`es, + /// there is no requirement for them to be combined into one. + Reject(usize, usize), + /// Expresses that every byte of the haystack has been visited, ending + /// the iteration. + Done, +} + +/// A searcher for a string pattern. +/// +/// This trait provides methods for searching for non-overlapping +/// matches of a pattern starting from the front (left) of a string. +/// +/// It will be implemented by associated `Searcher` +/// types of the [`Pattern`] trait. +/// +/// The trait is marked unsafe because the indices returned by the +/// [`next()`][Searcher::next] methods are required to lie on valid split +/// positions of the haystack (for `&str` haystacks, these are utf8 character +/// boundaries). This enables consumers of this trait to slice the haystack +/// without additional runtime checks. +pub unsafe trait Searcher<'h, H: Haystack + ?Sized> { + /// Getter for the underlying haystack to be searched in + /// + /// Will always return the same haystack. + fn haystack(&self) -> &'h H; + + /// Performs the next search step starting from the front. + /// + /// - Returns [`Match(a, b)`][SearchStep::Match] if `haystack[a..b]` matches + /// the pattern. + /// - Returns [`Reject(a, b)`][SearchStep::Reject] if `haystack[a..b]` can + /// not match the pattern, even partially. + /// - Returns [`Done`][SearchStep::Done] if every byte of the haystack has + /// been visited. + /// + /// The stream of [`Match`][SearchStep::Match] and + /// [`Reject`][SearchStep::Reject] values up to a [`Done`][SearchStep::Done] + /// will contain index ranges that are adjacent, non-overlapping, + /// covering the whole haystack, and laying on valid split positions of the + /// haystack. + /// + /// A [`Match`][SearchStep::Match] result needs to contain the whole matched + /// pattern, however [`Reject`][SearchStep::Reject] results may be split up + /// into arbitrary many adjacent fragments. Both ranges may have zero length. + /// + /// As an example, the pattern `"aaa"` and the haystack `"cbaaaaab"` + /// might produce the stream + /// `[Reject(0, 1), Reject(1, 2), Match(2, 5), Reject(5, 8)]` + fn next(&mut self) -> SearchStep; + + /// Finds the next [`Match`][SearchStep::Match] result. See [`next()`][Searcher::next]. + /// + /// Unlike [`next()`][Searcher::next], there is no guarantee that the returned ranges + /// of this and [`next_reject`][Searcher::next_reject] will overlap. This will return + /// `(start_match, end_match)`, where start_match is the index of where + /// the match begins, and end_match is the index after the end of the match. + #[inline] + fn next_match(&mut self) -> Option<(usize, usize)> { + loop { + match self.next() { + SearchStep::Match(a, b) => return Some((a, b)), + SearchStep::Done => return None, + _ => continue, + } + } + } + + /// Finds the next [`Reject`][SearchStep::Reject] result. See [`next()`][Searcher::next] + /// and [`next_match()`][Searcher::next_match]. + /// + /// Unlike [`next()`][Searcher::next], there is no guarantee that the returned ranges + /// of this and [`next_match`][Searcher::next_match] will overlap. + #[inline] + fn next_reject(&mut self) -> Option<(usize, usize)> { + loop { + match self.next() { + SearchStep::Reject(a, b) => return Some((a, b)), + SearchStep::Done => return None, + _ => continue, + } + } + } +} + +/// A reverse searcher for a string pattern. +/// +/// This trait provides methods for searching for non-overlapping +/// matches of a pattern starting from the back (right) of a string. +/// +/// It will be implemented by associated [`Searcher`] +/// types of the [`Pattern`] trait if the pattern supports searching +/// for it from the back. +/// +/// The index ranges returned by this trait are not required +/// to exactly match those of the forward search in reverse. +/// +/// For the reason why this trait is marked unsafe, see the +/// parent trait [`Searcher`]. +pub unsafe trait ReverseSearcher<'h, H: Haystack + ?Sized>: Searcher<'h, H> { + /// Performs the next search step starting from the back. + /// + /// - Returns [`Match(a, b)`][SearchStep::Match] if `haystack[a..b]` + /// matches the pattern. + /// - Returns [`Reject(a, b)`][SearchStep::Reject] if `haystack[a..b]` + /// can not match the pattern, even partially. + /// - Returns [`Done`][SearchStep::Done] if every byte of the haystack + /// has been visited + /// + /// The stream of [`Match`][SearchStep::Match] and + /// [`Reject`][SearchStep::Reject] values up to a [`Done`][SearchStep::Done] + /// will contain index ranges that are adjacent, non-overlapping, + /// covering the whole haystack, and laying on valid split positions of the + /// haystack. + /// + /// A [`Match`][SearchStep::Match] result needs to contain the whole matched + /// pattern, however [`Reject`][SearchStep::Reject] results may be split up + /// into arbitrary many adjacent fragments. Both ranges may have zero length. + /// + /// As an example, the pattern `"aaa"` and the haystack `"cbaaaaab"` + /// might produce the stream + /// `[Reject(7, 8), Match(4, 7), Reject(1, 4), Reject(0, 1)]`. + fn next_back(&mut self) -> SearchStep; + + /// Finds the next [`Match`][SearchStep::Match] result. + /// See [`next_back()`][ReverseSearcher::next_back]. + #[inline] + fn next_match_back(&mut self) -> Option<(usize, usize)> { + loop { + match self.next_back() { + SearchStep::Match(a, b) => return Some((a, b)), + SearchStep::Done => return None, + _ => continue, + } + } + } + + /// Finds the next [`Reject`][SearchStep::Reject] result. + /// See [`next_back()`][ReverseSearcher::next_back]. + #[inline] + fn next_reject_back(&mut self) -> Option<(usize, usize)> { + loop { + match self.next_back() { + SearchStep::Reject(a, b) => return Some((a, b)), + SearchStep::Done => return None, + _ => continue, + } + } + } +} + +/// A marker trait to express that a [`ReverseSearcher`] +/// can be used for a [`DoubleEndedIterator`] implementation. +/// +/// For this, the impl of [`Searcher`] and [`ReverseSearcher`] need +/// to follow these conditions: +/// +/// - All results of `next()` need to be identical +/// to the results of `next_back()` in reverse order. +/// - `next()` and `next_back()` need to behave as +/// the two ends of a range of values, that is they +/// can not "walk past each other". +/// +/// # Examples +/// +/// `char::Searcher` is a `DoubleEndedSearcher` because searching for a +/// [`char`] only requires looking at one at a time, which behaves the same +/// from both ends. +/// +/// `(&str)::Searcher` is not a `DoubleEndedSearcher` because +/// the pattern `"aa"` in the haystack `"aaa"` matches as either +/// `"[aa]a"` or `"a[aa]"`, depending on which side it is searched. +pub trait DoubleEndedSearcher<'h, H: Haystack + ?Sized>: ReverseSearcher<'h, H> {} diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index 26c48d48d211e..a078db2cc3550 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -1,6 +1,5 @@ //! Iterators for `str` methods. -use super::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher}; use super::validations::{next_code_point, next_code_point_reverse}; use super::{ BytesIsNotEmpty, CharEscapeDebugContinue, CharEscapeDefault, CharEscapeUnicode, @@ -13,6 +12,7 @@ use crate::iter::{ }; use crate::num::NonZero; use crate::ops::Try; +use crate::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher}; use crate::slice::{self, Split as SliceSplit}; use crate::{char as char_mod, option}; @@ -416,7 +416,7 @@ macro_rules! derive_pattern_clone { (clone $t:ident with |$s:ident| $e:expr) => { impl<'a, P> Clone for $t<'a, P> where - P: Pattern: Clone>, + P: Pattern: Clone>, { fn clone(&self) -> Self { let $s = self; @@ -429,7 +429,7 @@ macro_rules! derive_pattern_clone { /// This macro generates two public iterator structs /// wrapping a private internal one that makes use of the `Pattern` API. /// -/// For all patterns `P: Pattern` the following items will be +/// For all patterns `P: Pattern` the following items will be /// generated (generics omitted): /// /// struct $forward_iterator($internal_iterator); @@ -489,12 +489,12 @@ macro_rules! generate_pattern_iterators { } => { $(#[$forward_iterator_attribute])* $(#[$common_stability_attribute])* - pub struct $forward_iterator<'a, P: Pattern>(pub(super) $internal_iterator<'a, P>); + pub struct $forward_iterator<'a, P: Pattern>(pub(super) $internal_iterator<'a, P>); $(#[$common_stability_attribute])* impl<'a, P> fmt::Debug for $forward_iterator<'a, P> where - P: Pattern: fmt::Debug>, + P: Pattern: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple(stringify!($forward_iterator)) @@ -504,7 +504,7 @@ macro_rules! generate_pattern_iterators { } $(#[$common_stability_attribute])* - impl<'a, P: Pattern> Iterator for $forward_iterator<'a, P> { + impl<'a, P: Pattern> Iterator for $forward_iterator<'a, P> { type Item = $iterty; #[inline] @@ -516,7 +516,7 @@ macro_rules! generate_pattern_iterators { $(#[$common_stability_attribute])* impl<'a, P> Clone for $forward_iterator<'a, P> where - P: Pattern: Clone>, + P: Pattern: Clone>, { fn clone(&self) -> Self { $forward_iterator(self.0.clone()) @@ -525,12 +525,12 @@ macro_rules! generate_pattern_iterators { $(#[$reverse_iterator_attribute])* $(#[$common_stability_attribute])* - pub struct $reverse_iterator<'a, P: Pattern>(pub(super) $internal_iterator<'a, P>); + pub struct $reverse_iterator<'a, P: Pattern>(pub(super) $internal_iterator<'a, P>); $(#[$common_stability_attribute])* impl<'a, P> fmt::Debug for $reverse_iterator<'a, P> where - P: Pattern: fmt::Debug>, + P: Pattern: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple(stringify!($reverse_iterator)) @@ -542,7 +542,7 @@ macro_rules! generate_pattern_iterators { $(#[$common_stability_attribute])* impl<'a, P> Iterator for $reverse_iterator<'a, P> where - P: Pattern: ReverseSearcher<'a>>, + P: Pattern: ReverseSearcher<'a, str>>, { type Item = $iterty; @@ -555,7 +555,7 @@ macro_rules! generate_pattern_iterators { $(#[$common_stability_attribute])* impl<'a, P> Clone for $reverse_iterator<'a, P> where - P: Pattern: Clone>, + P: Pattern: Clone>, { fn clone(&self) -> Self { $reverse_iterator(self.0.clone()) @@ -563,12 +563,12 @@ macro_rules! generate_pattern_iterators { } #[stable(feature = "fused", since = "1.26.0")] - impl<'a, P: Pattern> FusedIterator for $forward_iterator<'a, P> {} + impl<'a, P: Pattern> FusedIterator for $forward_iterator<'a, P> {} #[stable(feature = "fused", since = "1.26.0")] impl<'a, P> FusedIterator for $reverse_iterator<'a, P> where - P: Pattern: ReverseSearcher<'a>>, + P: Pattern: ReverseSearcher<'a, str>>, {} generate_pattern_iterators!($($t)* with $(#[$common_stability_attribute])*, @@ -583,7 +583,7 @@ macro_rules! generate_pattern_iterators { $(#[$common_stability_attribute])* impl<'a, P> DoubleEndedIterator for $forward_iterator<'a, P> where - P: Pattern: DoubleEndedSearcher<'a>>, + P: Pattern: DoubleEndedSearcher<'a, str>>, { #[inline] fn next_back(&mut self) -> Option<$iterty> { @@ -594,7 +594,7 @@ macro_rules! generate_pattern_iterators { $(#[$common_stability_attribute])* impl<'a, P> DoubleEndedIterator for $reverse_iterator<'a, P> where - P: Pattern: DoubleEndedSearcher<'a>>, + P: Pattern: DoubleEndedSearcher<'a, str>>, { #[inline] fn next_back(&mut self) -> Option<$iterty> { @@ -614,7 +614,7 @@ derive_pattern_clone! { with |s| SplitInternal { matcher: s.matcher.clone(), ..*s } } -pub(super) struct SplitInternal<'a, P: Pattern> { +pub(super) struct SplitInternal<'a, P: Pattern> { pub(super) start: usize, pub(super) end: usize, pub(super) matcher: P::Searcher<'a>, @@ -624,7 +624,7 @@ pub(super) struct SplitInternal<'a, P: Pattern> { impl<'a, P> fmt::Debug for SplitInternal<'a, P> where - P: Pattern: fmt::Debug>, + P: Pattern: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SplitInternal") @@ -637,7 +637,7 @@ where } } -impl<'a, P: Pattern> SplitInternal<'a, P> { +impl<'a, P: Pattern> SplitInternal<'a, P> { #[inline] fn get_end(&mut self) -> Option<&'a str> { if !self.finished { @@ -694,7 +694,7 @@ impl<'a, P: Pattern> SplitInternal<'a, P> { #[inline] fn next_back(&mut self) -> Option<&'a str> where - P::Searcher<'a>: ReverseSearcher<'a>, + P::Searcher<'a>: ReverseSearcher<'a, str>, { if self.finished { return None; @@ -731,7 +731,7 @@ impl<'a, P: Pattern> SplitInternal<'a, P> { #[inline] fn next_back_inclusive(&mut self) -> Option<&'a str> where - P::Searcher<'a>: ReverseSearcher<'a>, + P::Searcher<'a>: ReverseSearcher<'a, str>, { if self.finished { return None; @@ -801,7 +801,7 @@ generate_pattern_iterators! { delegate double ended; } -impl<'a, P: Pattern> Split<'a, P> { +impl<'a, P: Pattern> Split<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -824,7 +824,7 @@ impl<'a, P: Pattern> Split<'a, P> { } } -impl<'a, P: Pattern> RSplit<'a, P> { +impl<'a, P: Pattern> RSplit<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -865,7 +865,7 @@ generate_pattern_iterators! { delegate double ended; } -impl<'a, P: Pattern> SplitTerminator<'a, P> { +impl<'a, P: Pattern> SplitTerminator<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -888,7 +888,7 @@ impl<'a, P: Pattern> SplitTerminator<'a, P> { } } -impl<'a, P: Pattern> RSplitTerminator<'a, P> { +impl<'a, P: Pattern> RSplitTerminator<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -916,7 +916,7 @@ derive_pattern_clone! { with |s| SplitNInternal { iter: s.iter.clone(), ..*s } } -pub(super) struct SplitNInternal<'a, P: Pattern> { +pub(super) struct SplitNInternal<'a, P: Pattern> { pub(super) iter: SplitInternal<'a, P>, /// The number of splits remaining pub(super) count: usize, @@ -924,7 +924,7 @@ pub(super) struct SplitNInternal<'a, P: Pattern> { impl<'a, P> fmt::Debug for SplitNInternal<'a, P> where - P: Pattern: fmt::Debug>, + P: Pattern: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SplitNInternal") @@ -934,7 +934,7 @@ where } } -impl<'a, P: Pattern> SplitNInternal<'a, P> { +impl<'a, P: Pattern> SplitNInternal<'a, P> { #[inline] fn next(&mut self) -> Option<&'a str> { match self.count { @@ -953,7 +953,7 @@ impl<'a, P: Pattern> SplitNInternal<'a, P> { #[inline] fn next_back(&mut self) -> Option<&'a str> where - P::Searcher<'a>: ReverseSearcher<'a>, + P::Searcher<'a>: ReverseSearcher<'a, str>, { match self.count { 0 => None, @@ -992,7 +992,7 @@ generate_pattern_iterators! { delegate single ended; } -impl<'a, P: Pattern> SplitN<'a, P> { +impl<'a, P: Pattern> SplitN<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -1015,7 +1015,7 @@ impl<'a, P: Pattern> SplitN<'a, P> { } } -impl<'a, P: Pattern> RSplitN<'a, P> { +impl<'a, P: Pattern> RSplitN<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -1043,18 +1043,18 @@ derive_pattern_clone! { with |s| MatchIndicesInternal(s.0.clone()) } -pub(super) struct MatchIndicesInternal<'a, P: Pattern>(pub(super) P::Searcher<'a>); +pub(super) struct MatchIndicesInternal<'a, P: Pattern>(pub(super) P::Searcher<'a>); impl<'a, P> fmt::Debug for MatchIndicesInternal<'a, P> where - P: Pattern: fmt::Debug>, + P: Pattern: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("MatchIndicesInternal").field(&self.0).finish() } } -impl<'a, P: Pattern> MatchIndicesInternal<'a, P> { +impl<'a, P: Pattern> MatchIndicesInternal<'a, P> { #[inline] fn next(&mut self) -> Option<(usize, &'a str)> { self.0 @@ -1066,7 +1066,7 @@ impl<'a, P: Pattern> MatchIndicesInternal<'a, P> { #[inline] fn next_back(&mut self) -> Option<(usize, &'a str)> where - P::Searcher<'a>: ReverseSearcher<'a>, + P::Searcher<'a>: ReverseSearcher<'a, str>, { self.0 .next_match_back() @@ -1098,18 +1098,18 @@ derive_pattern_clone! { with |s| MatchesInternal(s.0.clone()) } -pub(super) struct MatchesInternal<'a, P: Pattern>(pub(super) P::Searcher<'a>); +pub(super) struct MatchesInternal<'a, P: Pattern>(pub(super) P::Searcher<'a>); impl<'a, P> fmt::Debug for MatchesInternal<'a, P> where - P: Pattern: fmt::Debug>, + P: Pattern: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("MatchesInternal").field(&self.0).finish() } } -impl<'a, P: Pattern> MatchesInternal<'a, P> { +impl<'a, P: Pattern> MatchesInternal<'a, P> { #[inline] fn next(&mut self) -> Option<&'a str> { // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. @@ -1122,7 +1122,7 @@ impl<'a, P: Pattern> MatchesInternal<'a, P> { #[inline] fn next_back(&mut self) -> Option<&'a str> where - P::Searcher<'a>: ReverseSearcher<'a>, + P::Searcher<'a>: ReverseSearcher<'a, str>, { // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. self.0.next_match_back().map(|(a, b)| unsafe { @@ -1293,7 +1293,7 @@ pub struct SplitAsciiWhitespace<'a> { /// /// [`split_inclusive`]: str::split_inclusive #[stable(feature = "split_inclusive", since = "1.51.0")] -pub struct SplitInclusive<'a, P: Pattern>(pub(super) SplitInternal<'a, P>); +pub struct SplitInclusive<'a, P: Pattern>(pub(super) SplitInternal<'a, P>); #[stable(feature = "split_whitespace", since = "1.1.0")] impl<'a> Iterator for SplitWhitespace<'a> { @@ -1415,7 +1415,7 @@ impl<'a> SplitAsciiWhitespace<'a> { } #[stable(feature = "split_inclusive", since = "1.51.0")] -impl<'a, P: Pattern> Iterator for SplitInclusive<'a, P> { +impl<'a, P: Pattern> Iterator for SplitInclusive<'a, P> { type Item = &'a str; #[inline] @@ -1425,7 +1425,7 @@ impl<'a, P: Pattern> Iterator for SplitInclusive<'a, P> { } #[stable(feature = "split_inclusive", since = "1.51.0")] -impl<'a, P: Pattern: fmt::Debug>> fmt::Debug for SplitInclusive<'a, P> { +impl<'a, P: Pattern: fmt::Debug>> fmt::Debug for SplitInclusive<'a, P> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SplitInclusive").field("0", &self.0).finish() } @@ -1433,14 +1433,14 @@ impl<'a, P: Pattern: fmt::Debug>> fmt::Debug for SplitInclusive<'a, // FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "split_inclusive", since = "1.51.0")] -impl<'a, P: Pattern: Clone>> Clone for SplitInclusive<'a, P> { +impl<'a, P: Pattern: Clone>> Clone for SplitInclusive<'a, P> { fn clone(&self) -> Self { SplitInclusive(self.0.clone()) } } #[stable(feature = "split_inclusive", since = "1.51.0")] -impl<'a, P: Pattern: DoubleEndedSearcher<'a>>> DoubleEndedIterator +impl<'a, P: Pattern: DoubleEndedSearcher<'a, str>>> DoubleEndedIterator for SplitInclusive<'a, P> { #[inline] @@ -1450,9 +1450,9 @@ impl<'a, P: Pattern: DoubleEndedSearcher<'a>>> DoubleEndedIterator } #[stable(feature = "split_inclusive", since = "1.51.0")] -impl<'a, P: Pattern> FusedIterator for SplitInclusive<'a, P> {} +impl<'a, P: Pattern> FusedIterator for SplitInclusive<'a, P> {} -impl<'a, P: Pattern> SplitInclusive<'a, P> { +impl<'a, P: Pattern> SplitInclusive<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 79f4f29da2d43..1126a4c37deee 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -13,9 +13,9 @@ mod iter; mod traits; mod validations; -use self::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher}; use crate::char::{self, EscapeDebugExtArgs}; use crate::hint::assert_unchecked; +use crate::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher}; use crate::range::Range; use crate::slice::{self, SliceIndex}; use crate::ub_checks::assert_unsafe_precondition; @@ -1380,7 +1380,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn contains(&self, pat: P) -> bool { + pub fn contains<'a, P: Pattern>(&'a self, pat: P) -> bool { pat.is_contained_in(self) } @@ -1418,7 +1418,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "str_starts_with"] - pub fn starts_with(&self, pat: P) -> bool { + pub fn starts_with<'a, P: Pattern>(&'a self, pat: P) -> bool { pat.is_prefix_of(self) } @@ -1443,9 +1443,9 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "str_ends_with"] - pub fn ends_with(&self, pat: P) -> bool + pub fn ends_with<'a, P>(&'a self, pat: P) -> bool where - for<'a> P::Searcher<'a>: ReverseSearcher<'a>, + P: Pattern: ReverseSearcher<'a, str>>, { pat.is_suffix_of(self) } @@ -1494,7 +1494,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn find(&self, pat: P) -> Option { + pub fn find<'a, P: Pattern>(&'a self, pat: P) -> Option { pat.into_searcher(self).next_match().map(|(i, _)| i) } @@ -1540,9 +1540,9 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn rfind(&self, pat: P) -> Option + pub fn rfind<'a, P>(&'a self, pat: P) -> Option where - for<'a> P::Searcher<'a>: ReverseSearcher<'a>, + P: Pattern: ReverseSearcher<'a, str>>, { pat.into_searcher(self).next_match_back().map(|(i, _)| i) } @@ -1668,7 +1668,7 @@ impl str { /// [`split_whitespace`]: str::split_whitespace #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn split(&self, pat: P) -> Split<'_, P> { + pub fn split<'a, P: Pattern>(&'a self, pat: P) -> Split<'a, P> { Split(SplitInternal { start: 0, end: self.len(), @@ -1709,7 +1709,7 @@ impl str { /// ``` #[stable(feature = "split_inclusive", since = "1.51.0")] #[inline] - pub fn split_inclusive(&self, pat: P) -> SplitInclusive<'_, P> { + pub fn split_inclusive<'a, P: Pattern>(&'a self, pat: P) -> SplitInclusive<'a, P> { SplitInclusive(SplitInternal { start: 0, end: self.len(), @@ -1764,9 +1764,9 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn rsplit(&self, pat: P) -> RSplit<'_, P> + pub fn rsplit<'a, P>(&'a self, pat: P) -> RSplit<'a, P> where - for<'a> P::Searcher<'a>: ReverseSearcher<'a>, + P: Pattern: ReverseSearcher<'a, str>>, { RSplit(self.split(pat).0) } @@ -1813,7 +1813,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn split_terminator(&self, pat: P) -> SplitTerminator<'_, P> { + pub fn split_terminator<'a, P: Pattern>(&'a self, pat: P) -> SplitTerminator<'a, P> { SplitTerminator(SplitInternal { allow_trailing_empty: false, ..self.split(pat).0 }) } @@ -1859,9 +1859,9 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn rsplit_terminator(&self, pat: P) -> RSplitTerminator<'_, P> + pub fn rsplit_terminator<'a, P>(&'a self, pat: P) -> RSplitTerminator<'a, P> where - for<'a> P::Searcher<'a>: ReverseSearcher<'a>, + P: Pattern: ReverseSearcher<'a, str>>, { RSplitTerminator(self.split_terminator(pat).0) } @@ -1914,7 +1914,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn splitn(&self, n: usize, pat: P) -> SplitN<'_, P> { + pub fn splitn<'a, P: Pattern>(&'a self, n: usize, pat: P) -> SplitN<'a, P> { SplitN(SplitNInternal { iter: self.split(pat).0, count: n }) } @@ -1963,9 +1963,9 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn rsplitn(&self, n: usize, pat: P) -> RSplitN<'_, P> + pub fn rsplitn<'a, P>(&'a self, n: usize, pat: P) -> RSplitN<'a, P> where - for<'a> P::Searcher<'a>: ReverseSearcher<'a>, + P: Pattern: ReverseSearcher<'a, str>>, { RSplitN(self.splitn(n, pat).0) } @@ -1983,7 +1983,10 @@ impl str { /// ``` #[stable(feature = "str_split_once", since = "1.52.0")] #[inline] - pub fn split_once(&self, delimiter: P) -> Option<(&'_ str, &'_ str)> { + pub fn split_once<'a, P>(&'a self, delimiter: P) -> Option<(&'a str, &'a str)> + where + P: Pattern, + { let (start, end) = delimiter.into_searcher(self).next_match()?; // SAFETY: `Searcher` is known to return valid indices. unsafe { Some((self.get_unchecked(..start), self.get_unchecked(end..))) } @@ -2002,9 +2005,9 @@ impl str { /// ``` #[stable(feature = "str_split_once", since = "1.52.0")] #[inline] - pub fn rsplit_once(&self, delimiter: P) -> Option<(&'_ str, &'_ str)> + pub fn rsplit_once<'a, P>(&'a self, delimiter: P) -> Option<(&'a str, &'a str)> where - for<'a> P::Searcher<'a>: ReverseSearcher<'a>, + P: Pattern: ReverseSearcher<'a, str>>, { let (start, end) = delimiter.into_searcher(self).next_match_back()?; // SAFETY: `Searcher` is known to return valid indices. @@ -2042,7 +2045,7 @@ impl str { /// ``` #[stable(feature = "str_matches", since = "1.2.0")] #[inline] - pub fn matches(&self, pat: P) -> Matches<'_, P> { + pub fn matches<'a, P: Pattern>(&'a self, pat: P) -> Matches<'a, P> { Matches(MatchesInternal(pat.into_searcher(self))) } @@ -2076,9 +2079,9 @@ impl str { /// ``` #[stable(feature = "str_matches", since = "1.2.0")] #[inline] - pub fn rmatches(&self, pat: P) -> RMatches<'_, P> + pub fn rmatches<'a, P>(&'a self, pat: P) -> RMatches<'a, P> where - for<'a> P::Searcher<'a>: ReverseSearcher<'a>, + P: Pattern: ReverseSearcher<'a, str>>, { RMatches(self.matches(pat).0) } @@ -2120,7 +2123,10 @@ impl str { /// ``` #[stable(feature = "str_match_indices", since = "1.5.0")] #[inline] - pub fn match_indices(&self, pat: P) -> MatchIndices<'_, P> { + pub fn match_indices<'a, P>(&'a self, pat: P) -> MatchIndices<'a, P> + where + P: Pattern, + { MatchIndices(MatchIndicesInternal(pat.into_searcher(self))) } @@ -2160,9 +2166,9 @@ impl str { /// ``` #[stable(feature = "str_match_indices", since = "1.5.0")] #[inline] - pub fn rmatch_indices(&self, pat: P) -> RMatchIndices<'_, P> + pub fn rmatch_indices<'a, P>(&'a self, pat: P) -> RMatchIndices<'a, P> where - for<'a> P::Searcher<'a>: ReverseSearcher<'a>, + P: Pattern: ReverseSearcher<'a, str>>, { RMatchIndices(self.match_indices(pat).0) } @@ -2375,9 +2381,9 @@ impl str { #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - pub fn trim_matches(&self, pat: P) -> &str + pub fn trim_matches<'a, P>(&'a self, pat: P) -> &'a str where - for<'a> P::Searcher<'a>: DoubleEndedSearcher<'a>, + P: Pattern: DoubleEndedSearcher<'a, str>>, { let mut i = 0; let mut j = 0; @@ -2422,7 +2428,7 @@ impl str { #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "trim_direction", since = "1.30.0")] - pub fn trim_start_matches(&self, pat: P) -> &str { + pub fn trim_start_matches<'a, P: Pattern>(&'a self, pat: P) -> &'a str { let mut i = self.len(); let mut matcher = pat.into_searcher(self); if let Some((a, _)) = matcher.next_reject() { @@ -2456,7 +2462,10 @@ impl str { #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] #[stable(feature = "str_strip", since = "1.45.0")] - pub fn strip_prefix(&self, prefix: P) -> Option<&str> { + pub fn strip_prefix<'a, P>(&'a self, prefix: P) -> Option<&'a str> + where + P: Pattern, + { prefix.strip_prefix_of(self) } @@ -2484,9 +2493,9 @@ impl str { #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] #[stable(feature = "str_strip", since = "1.45.0")] - pub fn strip_suffix(&self, suffix: P) -> Option<&str> + pub fn strip_suffix<'a, P>(&'a self, suffix: P) -> Option<&'a str> where - for<'a> P::Searcher<'a>: ReverseSearcher<'a>, + P: Pattern: ReverseSearcher<'a, str>>, { suffix.strip_suffix_of(self) } @@ -2521,9 +2530,10 @@ impl str { #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] #[stable(feature = "strip_circumfix", since = "1.98.0")] - pub fn strip_circumfix(&self, prefix: P, suffix: S) -> Option<&str> + pub fn strip_circumfix<'a, P, S>(&'a self, prefix: P, suffix: S) -> Option<&'a str> where - for<'a> S::Searcher<'a>: ReverseSearcher<'a>, + S: Pattern: ReverseSearcher<'a, str>>, + P: Pattern, { self.strip_prefix(prefix)?.strip_suffix(suffix) } @@ -2561,7 +2571,10 @@ impl str { #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] #[unstable(feature = "trim_prefix_suffix", issue = "142312")] - pub fn trim_prefix(&self, prefix: P) -> &str { + pub fn trim_prefix<'a, P>(&'a self, prefix: P) -> &'a str + where + P: Pattern, + { prefix.strip_prefix_of(self).unwrap_or(self) } @@ -2598,9 +2611,9 @@ impl str { #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] #[unstable(feature = "trim_prefix_suffix", issue = "142312")] - pub fn trim_suffix(&self, suffix: P) -> &str + pub fn trim_suffix<'a, P>(&'a self, suffix: P) -> &'a str where - for<'a> P::Searcher<'a>: ReverseSearcher<'a>, + P: Pattern: ReverseSearcher<'a, str>>, { suffix.strip_suffix_of(self).unwrap_or(self) } @@ -2641,9 +2654,9 @@ impl str { #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "trim_direction", since = "1.30.0")] - pub fn trim_end_matches(&self, pat: P) -> &str + pub fn trim_end_matches<'a, P>(&'a self, pat: P) -> &'a str where - for<'a> P::Searcher<'a>: ReverseSearcher<'a>, + P: Pattern: ReverseSearcher<'a, str>>, { let mut j = 0; let mut matcher = pat.into_searcher(self); @@ -2685,7 +2698,10 @@ impl str { note = "superseded by `trim_start_matches`", suggestion = "trim_start_matches" )] - pub fn trim_left_matches(&self, pat: P) -> &str { + pub fn trim_left_matches<'a, P>(&'a self, pat: P) -> &'a str + where + P: Pattern, + { self.trim_start_matches(pat) } @@ -2728,9 +2744,9 @@ impl str { note = "superseded by `trim_end_matches`", suggestion = "trim_end_matches" )] - pub fn trim_right_matches(&self, pat: P) -> &str + pub fn trim_right_matches<'a, P>(&'a self, pat: P) -> &'a str where - for<'a> P::Searcher<'a>: ReverseSearcher<'a>, + P: Pattern: ReverseSearcher<'a, str>>, { self.trim_end_matches(pat) } diff --git a/library/core/src/str/pattern.rs b/library/core/src/str/pattern.rs index af48c2eae8dd6..ecd1dccb8878f 100644 --- a/library/core/src/str/pattern.rs +++ b/library/core/src/str/pattern.rs @@ -1,36 +1,47 @@ -//! The string Pattern API. +//! [The Pattern API] implementation for searching in `&str`. //! -//! The Pattern API provides a generic mechanism for using different pattern -//! types when searching through a string. +//! The implementation provides generic mechanism for using different pattern +//! types when searching through a string. Although this API is unstable, it is +//! exposed via stable APIs on the [`str`] type. //! -//! For more details, see the traits [`Pattern`], [`Searcher`], -//! [`ReverseSearcher`], and [`DoubleEndedSearcher`]. +//! Depending on the type of the pattern, the behaviour of methods like +//! [`str::find`] and [`str::contains`] can change. The table below describes +//! some of those behaviours. //! -//! Although this API is unstable, it is exposed via stable APIs on the -//! [`str`] type. +//! | Pattern type | Match condition | +//! |--------------------------|-------------------------------------------| +//! | `&str` | is substring | +//! | `char` | is contained in string | +//! | `&[char]` | any char in slice is contained in string | +//! | `F: FnMut(char) -> bool` | `F` returns `true` for a char in string | +//! | `&&str` | is substring | +//! | `&String` | is substring | //! //! # Examples //! -//! [`Pattern`] is [implemented][pattern-impls] in the stable API for -//! [`&str`][`str`], [`char`], slices of [`char`], and functions and closures -//! implementing `FnMut(char) -> bool`. -//! //! ``` //! let s = "Can you find a needle in a haystack?"; //! //! // &str pattern //! assert_eq!(s.find("you"), Some(4)); +//! assert_eq!(s.find("thou"), None); +//! //! // char pattern //! assert_eq!(s.find('n'), Some(2)); -//! // array of chars pattern +//! assert_eq!(s.find('N'), None); +//! +//! // Array of chars pattern and slices thereof //! assert_eq!(s.find(&['a', 'e', 'i', 'o', 'u']), Some(1)); -//! // slice of chars pattern //! assert_eq!(s.find(&['a', 'e', 'i', 'o', 'u'][..]), Some(1)); -//! // closure pattern +//! assert_eq!(s.find(&['q', 'v', 'x']), None); +//! +//! // Predicate closure //! assert_eq!(s.find(|c: char| c.is_ascii_punctuation()), Some(35)); +//! assert_eq!(s.find(|c: char| c.is_lowercase()), Some(1)); +//! assert_eq!(s.find(|c: char| !c.is_ascii()), None); //! ``` //! -//! [pattern-impls]: Pattern#implementors +//! [The Pattern API]: crate::pattern #![unstable( feature = "pattern", @@ -40,332 +51,44 @@ use crate::cmp::Ordering; use crate::convert::TryInto as _; +use crate::ops::Range; +pub use crate::pattern::{ + DoubleEndedSearcher, Haystack, Pattern, ReverseSearcher, SearchStep, Searcher, Utf8Pattern, +}; use crate::slice::memchr; use crate::{cmp, fmt}; -// Pattern - -/// A string pattern. -/// -/// A `Pattern` expresses that the implementing type -/// can be used as a string pattern for searching in a [`&str`][str]. -/// -/// For example, both `'a'` and `"aa"` are patterns that -/// would match at index `1` in the string `"baaaab"`. -/// -/// The trait itself acts as a builder for an associated -/// [`Searcher`] type, which does the actual work of finding -/// occurrences of the pattern in a string. -/// -/// Depending on the type of the pattern, the behavior of methods like -/// [`str::find`] and [`str::contains`] can change. The table below describes -/// some of those behaviors. -/// -/// | Pattern type | Match condition | -/// |--------------------------|-------------------------------------------| -/// | `&str` | is substring | -/// | `char` | is contained in string | -/// | `&[char]` | any char in slice is contained in string | -/// | `F: FnMut(char) -> bool` | `F` returns `true` for a char in string | -/// | `&&str` | is substring | -/// | `&String` | is substring | -/// -/// # Examples -/// -/// ``` -/// // &str -/// assert_eq!("abaaa".find("ba"), Some(1)); -/// assert_eq!("abaaa".find("bac"), None); -/// -/// // char -/// assert_eq!("abaaa".find('a'), Some(0)); -/// assert_eq!("abaaa".find('b'), Some(1)); -/// assert_eq!("abaaa".find('c'), None); -/// -/// // &[char; N] -/// assert_eq!("ab".find(&['b', 'a']), Some(0)); -/// assert_eq!("abaaa".find(&['a', 'z']), Some(0)); -/// assert_eq!("abaaa".find(&['c', 'd']), None); -/// -/// // &[char] -/// assert_eq!("ab".find(&['b', 'a'][..]), Some(0)); -/// assert_eq!("abaaa".find(&['a', 'z'][..]), Some(0)); -/// assert_eq!("abaaa".find(&['c', 'd'][..]), None); -/// -/// // FnMut(char) -> bool -/// assert_eq!("abcdef_z".find(|ch| ch > 'd' && ch < 'y'), Some(4)); -/// assert_eq!("abcddd_z".find(|ch| ch > 'd' && ch < 'y'), None); -/// ``` -pub trait Pattern: Sized { - /// Associated searcher for this pattern - type Searcher<'a>: Searcher<'a>; - - /// Constructs the associated searcher from - /// `self` and the `haystack` to search in. - fn into_searcher(self, haystack: &str) -> Self::Searcher<'_>; - - /// Checks whether the pattern matches anywhere in the haystack - #[inline] - fn is_contained_in(self, haystack: &str) -> bool { - self.into_searcher(haystack).next_match().is_some() - } - - /// Checks whether the pattern matches at the front of the haystack - #[inline] - fn is_prefix_of(self, haystack: &str) -> bool { - matches!(self.into_searcher(haystack).next(), SearchStep::Match(0, _)) - } - - /// Checks whether the pattern matches at the back of the haystack - #[inline] - fn is_suffix_of<'a>(self, haystack: &'a str) -> bool - where - Self::Searcher<'a>: ReverseSearcher<'a>, - { - matches!(self.into_searcher(haystack).next_back(), SearchStep::Match(_, j) if haystack.len() == j) - } - - /// Removes the pattern from the front of haystack, if it matches. - #[inline] - fn strip_prefix_of(self, haystack: &str) -> Option<&str> { - if let SearchStep::Match(start, len) = self.into_searcher(haystack).next() { - debug_assert_eq!( - start, 0, - "The first search step from Searcher \ - must include the first character" - ); - // SAFETY: `Searcher` is known to return valid indices. - unsafe { Some(haystack.get_unchecked(len..)) } - } else { - None - } - } - - /// Removes the pattern from the back of haystack, if it matches. - #[inline] - fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&'a str> - where - Self::Searcher<'a>: ReverseSearcher<'a>, - { - if let SearchStep::Match(start, end) = self.into_searcher(haystack).next_back() { - debug_assert_eq!( - end, - haystack.len(), - "The first search step from ReverseSearcher \ - must include the last character" - ); - // SAFETY: `Searcher` is known to return valid indices. - unsafe { Some(haystack.get_unchecked(..start)) } - } else { - None - } - } - - /// Returns the pattern as UTF-8 if possible. - fn as_utf8_pattern(&self) -> Option> { - None - } -} -/// Result of calling [`Pattern::as_utf8_pattern()`]. -/// Can be used for inspecting the contents of a [`Pattern`] in cases -/// where the underlying representation can be represented as UTF-8. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum Utf8Pattern<'a> { - /// Type returned by String and str types. - /// This stores `str` rather than bytes so callers cannot describe - /// non-UTF-8 string patterns through this API. - StringPattern(&'a str), - /// Type returned by char types. - CharPattern(char), -} - -// Searcher - -/// Result of calling [`Searcher::next()`] or [`ReverseSearcher::next_back()`]. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum SearchStep { - /// Expresses that a match of the pattern has been found at - /// `haystack[a..b]`. - Match(usize, usize), - /// Expresses that `haystack[a..b]` has been rejected as a possible match - /// of the pattern. - /// - /// Note that there might be more than one `Reject` between two `Match`es, - /// there is no requirement for them to be combined into one. - Reject(usize, usize), - /// Expresses that every byte of the haystack has been visited, ending - /// the iteration. - Done, -} +///////////////////////////////////////////////////////////////////////////// +// Impl for Haystack +///////////////////////////////////////////////////////////////////////////// -/// A searcher for a string pattern. -/// -/// This trait provides methods for searching for non-overlapping -/// matches of a pattern starting from the front (left) of a string. -/// -/// It will be implemented by associated `Searcher` -/// types of the [`Pattern`] trait. -/// -/// The trait is marked unsafe because the indices returned by the -/// [`next()`][Searcher::next] methods are required to lie on valid utf8 -/// boundaries in the haystack. This enables consumers of this trait to -/// slice the haystack without additional runtime checks. -pub unsafe trait Searcher<'a> { - /// Getter for the underlying string to be searched in - /// - /// Will always return the same [`&str`][str]. - fn haystack(&self) -> &'a str; - - /// Performs the next search step starting from the front. - /// - /// - Returns [`Match(a, b)`][SearchStep::Match] if `haystack[a..b]` matches - /// the pattern. - /// - Returns [`Reject(a, b)`][SearchStep::Reject] if `haystack[a..b]` can - /// not match the pattern, even partially. - /// - Returns [`Done`][SearchStep::Done] if every byte of the haystack has - /// been visited. - /// - /// The stream of [`Match`][SearchStep::Match] and - /// [`Reject`][SearchStep::Reject] values up to a [`Done`][SearchStep::Done] - /// will contain index ranges that are adjacent, non-overlapping, - /// covering the whole haystack, and laying on utf8 boundaries. - /// - /// A [`Match`][SearchStep::Match] result needs to contain the whole matched - /// pattern, however [`Reject`][SearchStep::Reject] results may be split up - /// into arbitrary many adjacent fragments. Both ranges may have zero length. - /// - /// As an example, the pattern `"aaa"` and the haystack `"cbaaaaab"` - /// might produce the stream - /// `[Reject(0, 1), Reject(1, 2), Match(2, 5), Reject(5, 8)]` - fn next(&mut self) -> SearchStep; - - /// Finds the next [`Match`][SearchStep::Match] result. See [`next()`][Searcher::next]. - /// - /// Unlike [`next()`][Searcher::next], there is no guarantee that the returned ranges - /// of this and [`next_reject`][Searcher::next_reject] will overlap. This will return - /// `(start_match, end_match)`, where start_match is the index of where - /// the match begins, and end_match is the index after the end of the match. - #[inline] - fn next_match(&mut self) -> Option<(usize, usize)> { - loop { - match self.next() { - SearchStep::Match(a, b) => return Some((a, b)), - SearchStep::Done => return None, - _ => continue, - } - } +impl Haystack for str { + #[inline(always)] + fn cursor_at_front(&self) -> usize { + 0 } - - /// Finds the next [`Reject`][SearchStep::Reject] result. See [`next()`][Searcher::next] - /// and [`next_match()`][Searcher::next_match]. - /// - /// Unlike [`next()`][Searcher::next], there is no guarantee that the returned ranges - /// of this and [`next_match`][Searcher::next_match] will overlap. - #[inline] - fn next_reject(&mut self) -> Option<(usize, usize)> { - loop { - match self.next() { - SearchStep::Reject(a, b) => return Some((a, b)), - SearchStep::Done => return None, - _ => continue, - } - } + #[inline(always)] + fn cursor_at_back(&self) -> usize { + self.len() } -} -/// A reverse searcher for a string pattern. -/// -/// This trait provides methods for searching for non-overlapping -/// matches of a pattern starting from the back (right) of a string. -/// -/// It will be implemented by associated [`Searcher`] -/// types of the [`Pattern`] trait if the pattern supports searching -/// for it from the back. -/// -/// The index ranges returned by this trait are not required -/// to exactly match those of the forward search in reverse. -/// -/// For the reason why this trait is marked unsafe, see the -/// parent trait [`Searcher`]. -pub unsafe trait ReverseSearcher<'a>: Searcher<'a> { - /// Performs the next search step starting from the back. - /// - /// - Returns [`Match(a, b)`][SearchStep::Match] if `haystack[a..b]` - /// matches the pattern. - /// - Returns [`Reject(a, b)`][SearchStep::Reject] if `haystack[a..b]` - /// can not match the pattern, even partially. - /// - Returns [`Done`][SearchStep::Done] if every byte of the haystack - /// has been visited - /// - /// The stream of [`Match`][SearchStep::Match] and - /// [`Reject`][SearchStep::Reject] values up to a [`Done`][SearchStep::Done] - /// will contain index ranges that are adjacent, non-overlapping, - /// covering the whole haystack, and laying on utf8 boundaries. - /// - /// A [`Match`][SearchStep::Match] result needs to contain the whole matched - /// pattern, however [`Reject`][SearchStep::Reject] results may be split up - /// into arbitrary many adjacent fragments. Both ranges may have zero length. - /// - /// As an example, the pattern `"aaa"` and the haystack `"cbaaaaab"` - /// might produce the stream - /// `[Reject(7, 8), Match(4, 7), Reject(1, 4), Reject(0, 1)]`. - fn next_back(&mut self) -> SearchStep; - - /// Finds the next [`Match`][SearchStep::Match] result. - /// See [`next_back()`][ReverseSearcher::next_back]. - #[inline] - fn next_match_back(&mut self) -> Option<(usize, usize)> { - loop { - match self.next_back() { - SearchStep::Match(a, b) => return Some((a, b)), - SearchStep::Done => return None, - _ => continue, - } - } + #[inline(always)] + fn is_empty(&self) -> bool { + str::is_empty(self) } - /// Finds the next [`Reject`][SearchStep::Reject] result. - /// See [`next_back()`][ReverseSearcher::next_back]. - #[inline] - fn next_reject_back(&mut self) -> Option<(usize, usize)> { - loop { - match self.next_back() { - SearchStep::Reject(a, b) => return Some((a, b)), - SearchStep::Done => return None, - _ => continue, - } - } + #[inline(always)] + unsafe fn get_unchecked(&self, range: Range) -> &Self { + // SAFETY: Caller promises position is a character boundary. + unsafe { str::get_unchecked(self, range) } } } -/// A marker trait to express that a [`ReverseSearcher`] -/// can be used for a [`DoubleEndedIterator`] implementation. -/// -/// For this, the impl of [`Searcher`] and [`ReverseSearcher`] need -/// to follow these conditions: -/// -/// - All results of `next()` need to be identical -/// to the results of `next_back()` in reverse order. -/// - `next()` and `next_back()` need to behave as -/// the two ends of a range of values, that is they -/// can not "walk past each other". -/// -/// # Examples -/// -/// `char::Searcher` is a `DoubleEndedSearcher` because searching for a -/// [`char`] only requires looking at one at a time, which behaves the same -/// from both ends. -/// -/// `(&str)::Searcher` is not a `DoubleEndedSearcher` because -/// the pattern `"aa"` in the haystack `"aaa"` matches as either -/// `"[aa]a"` or `"a[aa]"`, depending on which side it is searched. -pub trait DoubleEndedSearcher<'a>: ReverseSearcher<'a> {} - ///////////////////////////////////////////////////////////////////////////// // Impl for char ///////////////////////////////////////////////////////////////////////////// -/// Associated type for `::Searcher<'a>`. +/// Associated type for `>::Searcher`. #[derive(Clone, Debug)] pub struct CharSearcher<'a> { haystack: &'a str, @@ -398,7 +121,7 @@ impl CharSearcher<'_> { } } -unsafe impl<'a> Searcher<'a> for CharSearcher<'a> { +unsafe impl<'a> Searcher<'a, str> for CharSearcher<'a> { #[inline] fn haystack(&self) -> &'a str { self.haystack @@ -476,7 +199,7 @@ unsafe impl<'a> Searcher<'a> for CharSearcher<'a> { // let next_reject use the default implementation from the Searcher trait } -unsafe impl<'a> ReverseSearcher<'a> for CharSearcher<'a> { +unsafe impl<'a> ReverseSearcher<'a, str> for CharSearcher<'a> { #[inline] fn next_back(&mut self) -> SearchStep { let old_finger = self.finger_back; @@ -550,7 +273,7 @@ unsafe impl<'a> ReverseSearcher<'a> for CharSearcher<'a> { // let next_reject_back use the default implementation from the Searcher trait } -impl<'a> DoubleEndedSearcher<'a> for CharSearcher<'a> {} +impl<'a> DoubleEndedSearcher<'a, str> for CharSearcher<'a> {} /// Searches for chars that are equal to a given [`char`]. /// @@ -559,11 +282,11 @@ impl<'a> DoubleEndedSearcher<'a> for CharSearcher<'a> {} /// ``` /// assert_eq!("Hello world".find('o'), Some(4)); /// ``` -impl Pattern for char { - type Searcher<'a> = CharSearcher<'a>; +impl Pattern for char { + type Searcher<'h> = CharSearcher<'h>; #[inline] - fn into_searcher<'a>(self, haystack: &'a str) -> Self::Searcher<'a> { + fn into_searcher<'h>(self, haystack: &'h str) -> CharSearcher<'h> { let mut utf8_encoded = [0; char::MAX_LEN_UTF8]; let utf8_size = self .encode_utf8(&mut utf8_encoded) @@ -597,22 +320,22 @@ impl Pattern for char { } #[inline] - fn strip_prefix_of(self, haystack: &str) -> Option<&str> { + fn strip_prefix_of<'h>(self, haystack: &'h str) -> Option<&'h str> { self.encode_utf8(&mut [0u8; 4]).strip_prefix_of(haystack) } #[inline] - fn is_suffix_of<'a>(self, haystack: &'a str) -> bool + fn is_suffix_of<'h>(self, haystack: &'h str) -> bool where - Self::Searcher<'a>: ReverseSearcher<'a>, + Self::Searcher<'h>: ReverseSearcher<'h, str>, { self.encode_utf8(&mut [0u8; 4]).is_suffix_of(haystack) } #[inline] - fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&'a str> + fn strip_suffix_of<'h>(self, haystack: &'h str) -> Option<&'h str> where - Self::Searcher<'a>: ReverseSearcher<'a>, + Self::Searcher<'h>: ReverseSearcher<'h, str>, { self.encode_utf8(&mut [0u8; 4]).strip_suffix_of(haystack) } @@ -672,16 +395,16 @@ struct MultiCharEqSearcher<'a, C: MultiCharEq> { char_indices: super::CharIndices<'a>, } -impl Pattern for MultiCharEqPattern { - type Searcher<'a> = MultiCharEqSearcher<'a, C>; +impl Pattern for MultiCharEqPattern { + type Searcher<'h> = MultiCharEqSearcher<'h, C>; #[inline] - fn into_searcher(self, haystack: &str) -> MultiCharEqSearcher<'_, C> { + fn into_searcher<'h>(self, haystack: &'h str) -> MultiCharEqSearcher<'h, C> { MultiCharEqSearcher { haystack, char_eq: self.0, char_indices: haystack.char_indices() } } } -unsafe impl<'a, C: MultiCharEq> Searcher<'a> for MultiCharEqSearcher<'a, C> { +unsafe impl<'a, C: MultiCharEq> Searcher<'a, str> for MultiCharEqSearcher<'a, C> { #[inline] fn haystack(&self) -> &'a str { self.haystack @@ -706,7 +429,7 @@ unsafe impl<'a, C: MultiCharEq> Searcher<'a> for MultiCharEqSearcher<'a, C> { } } -unsafe impl<'a, C: MultiCharEq> ReverseSearcher<'a> for MultiCharEqSearcher<'a, C> { +unsafe impl<'a, C: MultiCharEq> ReverseSearcher<'a, str> for MultiCharEqSearcher<'a, C> { #[inline] fn next_back(&mut self) -> SearchStep { let s = &mut self.char_indices; @@ -726,7 +449,7 @@ unsafe impl<'a, C: MultiCharEq> ReverseSearcher<'a> for MultiCharEqSearcher<'a, } } -impl<'a, C: MultiCharEq> DoubleEndedSearcher<'a> for MultiCharEqSearcher<'a, C> {} +impl<'a, C: MultiCharEq> DoubleEndedSearcher<'a, str> for MultiCharEqSearcher<'a, C> {} ///////////////////////////////////////////////////////////////////////////// @@ -735,17 +458,17 @@ macro_rules! pattern_methods { type Searcher<$a> = $t; #[inline] - fn into_searcher<$a>(self, haystack: &$a str) -> $t { + fn into_searcher<$a>(self, haystack: &$a str) -> Self::Searcher<$a> { ($smap)(($pmap)(self).into_searcher(haystack)) } #[inline] - fn is_contained_in<$a>(self, haystack: &$a str) -> bool { + fn is_contained_in(self, haystack: &str) -> bool { ($pmap)(self).is_contained_in(haystack) } #[inline] - fn is_prefix_of<$a>(self, haystack: &$a str) -> bool { + fn is_prefix_of(self, haystack: &str) -> bool { ($pmap)(self).is_prefix_of(haystack) } @@ -757,7 +480,7 @@ macro_rules! pattern_methods { #[inline] fn is_suffix_of<$a>(self, haystack: &$a str) -> bool where - $t: ReverseSearcher<$a>, + $t: ReverseSearcher<$a, str>, { ($pmap)(self).is_suffix_of(haystack) } @@ -765,7 +488,7 @@ macro_rules! pattern_methods { #[inline] fn strip_suffix_of<$a>(self, haystack: &$a str) -> Option<&$a str> where - $t: ReverseSearcher<$a>, + $t: ReverseSearcher<$a, str>, { ($pmap)(self).strip_suffix_of(haystack) } @@ -807,16 +530,16 @@ macro_rules! searcher_methods { }; } -/// Associated type for `<[char; N] as Pattern>::Searcher<'a>`. +/// Associated type for `<[char; N] as Pattern>::Searcher`. #[derive(Clone, Debug)] pub struct CharArraySearcher<'a, const N: usize>( - as Pattern>::Searcher<'a>, + as Pattern>::Searcher<'a>, ); -/// Associated type for `<&[char; N] as Pattern>::Searcher<'a>`. +/// Associated type for `<&[char; N] as Pattern>::Searcher`. #[derive(Clone, Debug)] pub struct CharArrayRefSearcher<'a, 'b, const N: usize>( - as Pattern>::Searcher<'a>, + as Pattern>::Searcher<'a>, ); /// Searches for chars that are equal to any of the [`char`]s in the array. @@ -827,19 +550,19 @@ pub struct CharArrayRefSearcher<'a, 'b, const N: usize>( /// assert_eq!("Hello world".find(['o', 'l']), Some(2)); /// assert_eq!("Hello world".find(['h', 'w']), Some(6)); /// ``` -impl Pattern for [char; N] { +impl Pattern for [char; N] { pattern_methods!('a, CharArraySearcher<'a, N>, MultiCharEqPattern, CharArraySearcher); } -unsafe impl<'a, const N: usize> Searcher<'a> for CharArraySearcher<'a, N> { +unsafe impl<'a, const N: usize> Searcher<'a, str> for CharArraySearcher<'a, N> { searcher_methods!(forward); } -unsafe impl<'a, const N: usize> ReverseSearcher<'a> for CharArraySearcher<'a, N> { +unsafe impl<'a, const N: usize> ReverseSearcher<'a, str> for CharArraySearcher<'a, N> { searcher_methods!(reverse); } -impl<'a, const N: usize> DoubleEndedSearcher<'a> for CharArraySearcher<'a, N> {} +impl<'a, const N: usize> DoubleEndedSearcher<'a, str> for CharArraySearcher<'a, N> {} /// Searches for chars that are equal to any of the [`char`]s in the array. /// @@ -849,19 +572,19 @@ impl<'a, const N: usize> DoubleEndedSearcher<'a> for CharArraySearcher<'a, N> {} /// assert_eq!("Hello world".find(&['o', 'l']), Some(2)); /// assert_eq!("Hello world".find(&['h', 'w']), Some(6)); /// ``` -impl<'b, const N: usize> Pattern for &'b [char; N] { +impl<'b, const N: usize> Pattern for &'b [char; N] { pattern_methods!('a, CharArrayRefSearcher<'a, 'b, N>, MultiCharEqPattern, CharArrayRefSearcher); } -unsafe impl<'a, 'b, const N: usize> Searcher<'a> for CharArrayRefSearcher<'a, 'b, N> { +unsafe impl<'a, 'b, const N: usize> Searcher<'a, str> for CharArrayRefSearcher<'a, 'b, N> { searcher_methods!(forward); } -unsafe impl<'a, 'b, const N: usize> ReverseSearcher<'a> for CharArrayRefSearcher<'a, 'b, N> { +unsafe impl<'a, 'b, const N: usize> ReverseSearcher<'a, str> for CharArrayRefSearcher<'a, 'b, N> { searcher_methods!(reverse); } -impl<'a, 'b, const N: usize> DoubleEndedSearcher<'a> for CharArrayRefSearcher<'a, 'b, N> {} +impl<'a, 'b, const N: usize> DoubleEndedSearcher<'a, str> for CharArrayRefSearcher<'a, 'b, N> {} ///////////////////////////////////////////////////////////////////////////// // Impl for &[char] @@ -869,19 +592,21 @@ impl<'a, 'b, const N: usize> DoubleEndedSearcher<'a> for CharArrayRefSearcher<'a // Todo: Change / Remove due to ambiguity in meaning. -/// Associated type for `<&[char] as Pattern>::Searcher<'a>`. +/// Associated type for `<&[char] as Pattern>::Searcher`. #[derive(Clone, Debug)] -pub struct CharSliceSearcher<'a, 'b>( as Pattern>::Searcher<'a>); +pub struct CharSliceSearcher<'a, 'b>( + as Pattern>::Searcher<'a>, +); -unsafe impl<'a, 'b> Searcher<'a> for CharSliceSearcher<'a, 'b> { +unsafe impl<'a, 'b> Searcher<'a, str> for CharSliceSearcher<'a, 'b> { searcher_methods!(forward); } -unsafe impl<'a, 'b> ReverseSearcher<'a> for CharSliceSearcher<'a, 'b> { +unsafe impl<'a, 'b> ReverseSearcher<'a, str> for CharSliceSearcher<'a, 'b> { searcher_methods!(reverse); } -impl<'a, 'b> DoubleEndedSearcher<'a> for CharSliceSearcher<'a, 'b> {} +impl<'a, 'b> DoubleEndedSearcher<'a, str> for CharSliceSearcher<'a, 'b> {} /// Searches for chars that are equal to any of the [`char`]s in the slice. /// @@ -891,7 +616,7 @@ impl<'a, 'b> DoubleEndedSearcher<'a> for CharSliceSearcher<'a, 'b> {} /// assert_eq!("Hello world".find(&['o', 'l'][..]), Some(2)); /// assert_eq!("Hello world".find(&['h', 'w'][..]), Some(6)); /// ``` -impl<'b> Pattern for &'b [char] { +impl<'b> Pattern for &'b [char] { pattern_methods!('a, CharSliceSearcher<'a, 'b>, MultiCharEqPattern, CharSliceSearcher); } @@ -899,9 +624,9 @@ impl<'b> Pattern for &'b [char] { // Impl for F: FnMut(char) -> bool ///////////////////////////////////////////////////////////////////////////// -/// Associated type for `::Searcher<'a>`. +/// Associated type for `>::Searcher`. #[derive(Clone)] -pub struct CharPredicateSearcher<'a, F>( as Pattern>::Searcher<'a>) +pub struct CharPredicateSearcher<'a, F>( as Pattern>::Searcher<'a>) where F: FnMut(char) -> bool; @@ -916,21 +641,21 @@ where .finish() } } -unsafe impl<'a, F> Searcher<'a> for CharPredicateSearcher<'a, F> +unsafe impl<'a, F> Searcher<'a, str> for CharPredicateSearcher<'a, F> where F: FnMut(char) -> bool, { searcher_methods!(forward); } -unsafe impl<'a, F> ReverseSearcher<'a> for CharPredicateSearcher<'a, F> +unsafe impl<'a, F> ReverseSearcher<'a, str> for CharPredicateSearcher<'a, F> where F: FnMut(char) -> bool, { searcher_methods!(reverse); } -impl<'a, F> DoubleEndedSearcher<'a> for CharPredicateSearcher<'a, F> where F: FnMut(char) -> bool {} +impl<'a, F: FnMut(char) -> bool> DoubleEndedSearcher<'a, str> for CharPredicateSearcher<'a, F> {} /// Searches for [`char`]s that match the given predicate. /// @@ -940,7 +665,7 @@ impl<'a, F> DoubleEndedSearcher<'a> for CharPredicateSearcher<'a, F> where F: Fn /// assert_eq!("Hello world".find(char::is_uppercase), Some(0)); /// assert_eq!("Hello world".find(|c| "aeiou".contains(c)), Some(1)); /// ``` -impl Pattern for F +impl Pattern for F where F: FnMut(char) -> bool, { @@ -952,7 +677,7 @@ where ///////////////////////////////////////////////////////////////////////////// /// Delegates to the `&str` impl. -impl<'b, 'c> Pattern for &'c &'b str { +impl<'b, 'c> Pattern for &'c &'b str { pattern_methods!('a, StrSearcher<'a, 'b>, |&s| s, |s| s); } @@ -970,11 +695,11 @@ impl<'b, 'c> Pattern for &'c &'b str { /// ``` /// assert_eq!("Hello world".find("world"), Some(6)); /// ``` -impl<'b> Pattern for &'b str { - type Searcher<'a> = StrSearcher<'a, 'b>; +impl<'b> Pattern for &'b str { + type Searcher<'h> = StrSearcher<'h, 'b>; #[inline] - fn into_searcher(self, haystack: &str) -> StrSearcher<'_, 'b> { + fn into_searcher<'h>(self, haystack: &'h str) -> StrSearcher<'h, 'b> { StrSearcher::new(haystack, self) } @@ -1016,7 +741,7 @@ impl<'b> Pattern for &'b str { /// Removes the pattern from the front of haystack, if it matches. #[inline] - fn strip_prefix_of(self, haystack: &str) -> Option<&str> { + fn strip_prefix_of<'h>(self, haystack: &'h str) -> Option<&'h str> { if self.is_prefix_of(haystack) { // SAFETY: prefix was just verified to exist. unsafe { Some(haystack.get_unchecked(self.len()..)) } @@ -1027,18 +752,18 @@ impl<'b> Pattern for &'b str { /// Checks whether the pattern matches at the back of the haystack. #[inline] - fn is_suffix_of<'a>(self, haystack: &'a str) -> bool + fn is_suffix_of<'h>(self, haystack: &'h str) -> bool where - Self::Searcher<'a>: ReverseSearcher<'a>, + Self::Searcher<'h>: ReverseSearcher<'h, str>, { haystack.as_bytes().ends_with(self.as_bytes()) } /// Removes the pattern from the back of haystack, if it matches. #[inline] - fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&'a str> + fn strip_suffix_of<'h>(self, haystack: &'h str) -> Option<&'h str> where - Self::Searcher<'a>: ReverseSearcher<'a>, + Self::Searcher<'h>: ReverseSearcher<'h, str>, { if self.is_suffix_of(haystack) { let i = haystack.len() - self.len(); @@ -1060,7 +785,7 @@ impl<'b> Pattern for &'b str { ///////////////////////////////////////////////////////////////////////////// #[derive(Clone, Debug)] -/// Associated type for `<&str as Pattern>::Searcher<'a>`. +/// Associated type for `<&str as Pattern>::Searcher`. pub struct StrSearcher<'a, 'b> { haystack: &'a str, needle: &'b str, @@ -1128,7 +853,7 @@ impl<'a, 'b> StrSearcher<'a, 'b> { } } -unsafe impl<'a, 'b> Searcher<'a> for StrSearcher<'a, 'b> { +unsafe impl<'a, 'b> Searcher<'a, str> for StrSearcher<'a, 'b> { #[inline] fn haystack(&self) -> &'a str { self.haystack @@ -1250,7 +975,7 @@ unsafe impl<'a, 'b> Searcher<'a> for StrSearcher<'a, 'b> { } } -unsafe impl<'a, 'b> ReverseSearcher<'a> for StrSearcher<'a, 'b> { +unsafe impl<'a, 'b> ReverseSearcher<'a, str> for StrSearcher<'a, 'b> { #[inline] fn next_back(&mut self) -> SearchStep { match self.searcher { diff --git a/library/coretests/tests/pattern.rs b/library/coretests/tests/pattern.rs index b5cd019c59e76..85574bd07bac7 100644 --- a/library/coretests/tests/pattern.rs +++ b/library/coretests/tests/pattern.rs @@ -1,4 +1,4 @@ -use std::str::pattern::*; +use std::pattern::*; // This macro makes it easier to write // tests that do a series of iterations @@ -572,3 +572,82 @@ fn double_ended_regression_test() { next_match => Done ); } + +#[test] +fn two_way_next_reject_skips_matches() { + // `next_reject` must not report the matched regions as rejects + + // plen - pattern len + // ulen - unmatch len + // haystack is always a concatenation of [Match, Reject, Match] + #[track_caller] + fn check_fw_bw(haystack: &H, pat: T, plen: usize, ulen: usize) + where + H: Haystack + ?Sized, + T: Pattern + Copy, + for<'a> T::Searcher<'a>: ReverseSearcher<'a, H>, + { + // fragments + let f1 = (0, plen); + let f2 = (f1.1, f1.1 + ulen); + let f3 = (f2.1, f2.1 + plen); + + let mut searcher = pat.into_searcher(haystack); + let (start, end) = searcher.next_reject().expect("fw reject"); + assert_eq!(start, f2.0, "fw reject start"); + assert!(start < end && end <= f2.1, "fw reject must be a non-empty part of f2"); + assert_eq!(searcher.next_match(), Some(f3), "fw match"); + assert_eq!(searcher.next_match(), None, "fw end"); + + let mut searcher = pat.into_searcher(haystack); + let (start, end) = searcher.next_reject_back().expect("bw reject"); + assert_eq!(end, f2.1, "bw reject end"); + assert!(f2.0 <= start && start < end, "bw reject must be a non-empty part of f2"); + assert_eq!(searcher.next_match_back(), Some(f1), "bw match"); + assert_eq!(searcher.next_match_back(), None, "bw end"); + } + check_fw_bw("XYZabcXYZ", "XYZ", 3, 3); + + // single char 1..4 byte str + check_fw_bw("XabcX", "X", 1, 3); + check_fw_bw("\u{00e9}abc\u{00e9}", "\u{00e9}", 2, 3); + check_fw_bw("\u{20ac}abc\u{20ac}", "\u{20ac}", 3, 3); + check_fw_bw("\u{1f60a}abc\u{1f60a}", "\u{1f60a}", 4, 3); + + // single char 2..4 byte + check_fw_bw("\u{00e9}abc\u{00e9}", '\u{00e9}', 2, 3); + check_fw_bw("\u{20ac}abc\u{20ac}", '\u{20ac}', 3, 3); + check_fw_bw("\u{1f60a}abc\u{1f60a}", '\u{1f60a}', 4, 3); + + check_fw_bw("\u{00e9}abc\u{00e9}", ['\u{00e9}'], 2, 3); + check_fw_bw("\u{20ac}abc\u{20ac}", ['\u{20ac}'], 3, 3); + check_fw_bw("\u{1f60a}abc\u{1f60a}", ['\u{1f60a}'], 4, 3); + + check_fw_bw("\u{00e9}abc\u{00e9}", |c| c == '\u{00e9}', 2, 3); + check_fw_bw("\u{20ac}abc\u{20ac}", |c| c == '\u{20ac}', 3, 3); + check_fw_bw("\u{1f60a}abc\u{1f60a}", |c| c == '\u{1f60a}', 4, 3); +} + +#[test] +fn two_way_next_reject_never_reports_empty_reject() { + // A reject must never be an empty range + + // Haystack fully covered by the pattern: there are no rejects at all. + let mut searcher = "XYZ".into_searcher("XYZ"); + assert_eq!(searcher.next_reject(), None); + assert_eq!(searcher.next_reject(), None); + + let mut searcher = "XYZ".into_searcher("XYZ"); + assert_eq!(searcher.next_reject_back(), None); + assert_eq!(searcher.next_reject_back(), None); + + // Haystack ending with a match: the last reject is followed by `None`, + // not by an empty reject after the final match. + let mut searcher = "XYZ".into_searcher("XYZabcXYZ"); + assert_eq!(searcher.next_reject(), Some((3, 6))); + assert_eq!(searcher.next_reject(), None); + + let mut searcher = "XYZ".into_searcher("XYZabcXYZ"); + assert_eq!(searcher.next_reject_back(), Some((3, 6))); + assert_eq!(searcher.next_reject_back(), None); +} diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 980ec4416f04a..56ff07a001e86 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -568,6 +568,8 @@ pub use core::mem; pub use core::ops; #[stable(feature = "rust1", since = "1.0.0")] pub use core::option; +#[unstable(feature = "pattern", issue = "27721")] +pub use core::pattern; #[stable(feature = "pin", since = "1.33.0")] pub use core::pin; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/stdarch/crates/stdarch-gen-arm/src/wildstring.rs b/library/stdarch/crates/stdarch-gen-arm/src/wildstring.rs index 4f8cc67f5e019..5ff45e5ae7d53 100644 --- a/library/stdarch/crates/stdarch-gen-arm/src/wildstring.rs +++ b/library/stdarch/crates/stdarch-gen-arm/src/wildstring.rs @@ -2,7 +2,7 @@ use itertools::Itertools; use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt, quote}; use serde_with::{DeserializeFromStr, SerializeDisplay}; -use std::str::pattern::Pattern; +use std::pattern::Pattern; use std::{fmt, str::FromStr}; use crate::context::LocalContext; @@ -68,7 +68,7 @@ impl WildString { pub fn replace

(&self, from: P, to: &str) -> WildString where - P: Pattern + Copy, + P: for<'a> Pattern<&'a str> + Copy, { WildString( self.0 diff --git a/tests/ui/suggestions/issue-104961.fixed b/tests/ui/suggestions/issue-104961.fixed index 3019242880f82..09a561e959a9e 100644 --- a/tests/ui/suggestions/issue-104961.fixed +++ b/tests/ui/suggestions/issue-104961.fixed @@ -2,12 +2,12 @@ fn foo(x: &str) -> bool { x.starts_with(&("hi".to_string() + " you")) - //~^ ERROR the trait bound `String: Pattern` is not satisfied [E0277] + //~^ ERROR the trait bound `String: Pattern` is not satisfied [E0277] } fn foo2(x: &str) -> bool { x.starts_with(&"hi".to_string()) - //~^ ERROR the trait bound `String: Pattern` is not satisfied [E0277] + //~^ ERROR the trait bound `String: Pattern` is not satisfied [E0277] } fn main() { diff --git a/tests/ui/suggestions/issue-104961.rs b/tests/ui/suggestions/issue-104961.rs index b315e9bab0d1a..8805408be682f 100644 --- a/tests/ui/suggestions/issue-104961.rs +++ b/tests/ui/suggestions/issue-104961.rs @@ -2,12 +2,12 @@ fn foo(x: &str) -> bool { x.starts_with("hi".to_string() + " you") - //~^ ERROR the trait bound `String: Pattern` is not satisfied [E0277] + //~^ ERROR the trait bound `String: Pattern` is not satisfied [E0277] } fn foo2(x: &str) -> bool { x.starts_with("hi".to_string()) - //~^ ERROR the trait bound `String: Pattern` is not satisfied [E0277] + //~^ ERROR the trait bound `String: Pattern` is not satisfied [E0277] } fn main() { diff --git a/tests/ui/suggestions/issue-104961.stderr b/tests/ui/suggestions/issue-104961.stderr index 0d229e6dada5f..7f8ab697bf5d1 100644 --- a/tests/ui/suggestions/issue-104961.stderr +++ b/tests/ui/suggestions/issue-104961.stderr @@ -1,12 +1,12 @@ -error[E0277]: the trait bound `String: Pattern` is not satisfied +error[E0277]: the trait bound `String: Pattern` is not satisfied --> $DIR/issue-104961.rs:4:19 | LL | x.starts_with("hi".to_string() + " you") - | ----------- ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Pattern` is not implemented for `String` + | ----------- ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Pattern` is not implemented for `String` | | | required by a bound introduced by this call | - = note: required for `String` to implement `Pattern` + = note: required for `String` to implement `Pattern` note: required by a bound in `core::str::::starts_with` --> $SRC_DIR/core/src/str/mod.rs:LL:COL help: consider borrowing here @@ -14,15 +14,15 @@ help: consider borrowing here LL | x.starts_with(&("hi".to_string() + " you")) | ++ + -error[E0277]: the trait bound `String: Pattern` is not satisfied +error[E0277]: the trait bound `String: Pattern` is not satisfied --> $DIR/issue-104961.rs:9:19 | LL | x.starts_with("hi".to_string()) - | ----------- ^^^^^^^^^^^^^^^^ the trait `Pattern` is not implemented for `String` + | ----------- ^^^^^^^^^^^^^^^^ the trait `Pattern` is not implemented for `String` | | | required by a bound introduced by this call | - = note: required for `String` to implement `Pattern` + = note: required for `String` to implement `Pattern` note: required by a bound in `core::str::::starts_with` --> $SRC_DIR/core/src/str/mod.rs:LL:COL help: consider borrowing here diff --git a/tests/ui/suggestions/issue-62843.stderr b/tests/ui/suggestions/issue-62843.stderr index c3c0360b3a9d1..4c79548515c7b 100644 --- a/tests/ui/suggestions/issue-62843.stderr +++ b/tests/ui/suggestions/issue-62843.stderr @@ -1,12 +1,12 @@ -error[E0277]: the trait bound `String: Pattern` is not satisfied +error[E0277]: the trait bound `String: Pattern` is not satisfied --> $DIR/issue-62843.rs:4:32 | LL | println!("{:?}", line.find(pattern)); - | ---- ^^^^^^^ the trait `Pattern` is not implemented for `String` + | ---- ^^^^^^^ the trait `Pattern` is not implemented for `String` | | | required by a bound introduced by this call | - = note: required for `String` to implement `Pattern` + = note: required for `String` to implement `Pattern` note: required by a bound in `core::str::::find` --> $SRC_DIR/core/src/str/mod.rs:LL:COL help: consider borrowing here diff --git a/tests/ui/traits/bound/assoc-fn-bound-root-obligation.rs b/tests/ui/traits/bound/assoc-fn-bound-root-obligation.rs index 667d283bea3e4..1f30ab04871b9 100644 --- a/tests/ui/traits/bound/assoc-fn-bound-root-obligation.rs +++ b/tests/ui/traits/bound/assoc-fn-bound-root-obligation.rs @@ -1,10 +1,10 @@ fn strip_lf(s: &str) -> &str { s.strip_suffix(b'\n').unwrap_or(s) - //~^ ERROR the trait bound `u8: Pattern` is not satisfied + //~^ ERROR the trait bound `u8: Pattern` is not satisfied //~| NOTE required by a bound introduced by this call //~| NOTE the trait `FnMut(char)` is not implemented for `u8` - //~| HELP the following other types implement trait `Pattern`: - //~| NOTE required for `u8` to implement `Pattern` + //~| HELP the following other types implement trait `Pattern`: + //~| NOTE required for `u8` to implement `Pattern` //~| NOTE required by a bound in `core::str::::strip_suffix` } diff --git a/tests/ui/traits/bound/assoc-fn-bound-root-obligation.stderr b/tests/ui/traits/bound/assoc-fn-bound-root-obligation.stderr index 1cd62d2cbdbaa..3c767e5851021 100644 --- a/tests/ui/traits/bound/assoc-fn-bound-root-obligation.stderr +++ b/tests/ui/traits/bound/assoc-fn-bound-root-obligation.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `u8: Pattern` is not satisfied +error[E0277]: the trait bound `u8: Pattern` is not satisfied --> $DIR/assoc-fn-bound-root-obligation.rs:2:20 | LL | s.strip_suffix(b'\n').unwrap_or(s) @@ -6,7 +6,7 @@ LL | s.strip_suffix(b'\n').unwrap_or(s) | | | required by a bound introduced by this call | - = help: the following other types implement trait `Pattern`: + = help: the following other types implement trait `Pattern`: &'b String &'b [char; N] &'b [char] @@ -14,7 +14,7 @@ LL | s.strip_suffix(b'\n').unwrap_or(s) &'c &'b str [char; N] char - = note: required for `u8` to implement `Pattern` + = note: required for `u8` to implement `Pattern` note: required by a bound in `core::str::::strip_suffix` --> $SRC_DIR/core/src/str/mod.rs:LL:COL diff --git a/tests/ui/traits/suggest-dereferences/root-obligation.fixed b/tests/ui/traits/suggest-dereferences/root-obligation.fixed index ad0f184dc9a42..b3ca01febf6a9 100644 --- a/tests/ui/traits/suggest-dereferences/root-obligation.fixed +++ b/tests/ui/traits/suggest-dereferences/root-obligation.fixed @@ -4,7 +4,7 @@ fn get_vowel_count(string: &str) -> usize { string .chars() .filter(|c| "aeiou".contains(*c)) - //~^ ERROR the trait bound `&char: Pattern` is not satisfied + //~^ ERROR the trait bound `&char: Pattern` is not satisfied .count() } diff --git a/tests/ui/traits/suggest-dereferences/root-obligation.rs b/tests/ui/traits/suggest-dereferences/root-obligation.rs index a31a9955d313a..5e0206b806a9c 100644 --- a/tests/ui/traits/suggest-dereferences/root-obligation.rs +++ b/tests/ui/traits/suggest-dereferences/root-obligation.rs @@ -4,7 +4,7 @@ fn get_vowel_count(string: &str) -> usize { string .chars() .filter(|c| "aeiou".contains(c)) - //~^ ERROR the trait bound `&char: Pattern` is not satisfied + //~^ ERROR the trait bound `&char: Pattern` is not satisfied .count() } diff --git a/tests/ui/traits/suggest-dereferences/root-obligation.stderr b/tests/ui/traits/suggest-dereferences/root-obligation.stderr index dafd3469b6f03..95edd4818dcfe 100644 --- a/tests/ui/traits/suggest-dereferences/root-obligation.stderr +++ b/tests/ui/traits/suggest-dereferences/root-obligation.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `&char: Pattern` is not satisfied +error[E0277]: the trait bound `&char: Pattern` is not satisfied --> $DIR/root-obligation.rs:6:38 | LL | .filter(|c| "aeiou".contains(c)) @@ -7,7 +7,7 @@ LL | .filter(|c| "aeiou".contains(c)) | required by a bound introduced by this call | = note: required for `&char` to implement `FnOnce(char)` - = note: required for `&char` to implement `Pattern` + = note: required for `&char` to implement `Pattern` note: required by a bound in `core::str::::contains` --> $SRC_DIR/core/src/str/mod.rs:LL:COL help: consider dereferencing here