Skip to content

Commit 6a5148b

Browse files
committed
Introduce rsplit
1 parent 7f53b94 commit 6a5148b

File tree

3 files changed

+132
-3
lines changed

3 files changed

+132
-3
lines changed

src/libcollections/str.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ use slice::SliceConcatExt;
7474

7575
pub use core::str::{FromStr, Utf8Error, Str};
7676
pub use core::str::{Lines, LinesAny, MatchIndices, SplitStr, CharRange};
77-
pub use core::str::{Split, SplitTerminator};
78-
pub use core::str::{SplitN, RSplitN};
77+
pub use core::str::{Split, SplitTerminator, SplitN};
78+
pub use core::str::{RSplit, RSplitN};
7979
pub use core::str::{from_utf8, CharEq, Chars, CharIndices, Bytes};
8080
pub use core::str::{from_utf8_unchecked, from_c_str, ParseBoolError};
8181
pub use unicode::str::{Words, Graphemes, GraphemeIndices};
@@ -699,6 +699,34 @@ impl str {
699699
core_str::StrExt::split_terminator(&self[..], pat)
700700
}
701701

702+
/// An iterator over substrings of `self`, separated by a pattern,
703+
/// starting from the end of the string.
704+
///
705+
/// # Examples
706+
///
707+
/// Simple patterns:
708+
///
709+
/// ```
710+
/// let v: Vec<&str> = "Mary had a little lamb".rsplit(' ').collect();
711+
/// assert_eq!(v, ["lamb", "little", "a", "had", "Mary"]);
712+
///
713+
/// let v: Vec<&str> = "lion::tiger::leopard".rsplit("::").collect();
714+
/// assert_eq!(v, ["leopard", "tiger", "lion"]);
715+
/// ```
716+
///
717+
/// More complex patterns with a lambda:
718+
///
719+
/// ```
720+
/// let v: Vec<&str> = "abc1def2ghi".rsplit(|c: char| c.is_numeric()).collect();
721+
/// assert_eq!(v, ["ghi", "def", "abc"]);
722+
/// ```
723+
#[stable(feature = "rust1", since = "1.0.0")]
724+
pub fn rsplit<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplit<'a, P>
725+
where P::Searcher: ReverseSearcher<'a>
726+
{
727+
core_str::StrExt::rsplit(&self[..], pat)
728+
}
729+
702730
/// An iterator over substrings of `self`, separated by characters matched by a pattern,
703731
/// starting from the end of the string.
704732
///

src/libcollectionstest/str.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,20 @@ fn test_split_char_iterator_no_trailing() {
910910
assert_eq!(split, ["", "Märy häd ä little lämb", "Little lämb"]);
911911
}
912912

913+
#[test]
914+
fn test_rsplit() {
915+
let data = "\nMäry häd ä little lämb\nLittle lämb\n";
916+
917+
let split: Vec<&str> = data.rsplit(' ').collect();
918+
assert_eq!(split, ["lämb\n", "lämb\nLittle", "little", "ä", "häd", "\nMäry"]);
919+
920+
let split: Vec<&str> = data.rsplit("lämb").collect();
921+
assert_eq!(split, ["\n", "\nLittle ", "\nMäry häd ä little "]);
922+
923+
let split: Vec<&str> = data.rsplit(|c: char| c == 'ä').collect();
924+
assert_eq!(split, ["mb\n", "mb\nLittle l", " little l", "d ", "ry h", "\nM"]);
925+
}
926+
913927
#[test]
914928
fn test_words() {
915929
let data = "\n \tMäry häd\tä little lämb\nLittle lämb\n";

src/libcore/str/mod.rs

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,24 @@ macro_rules! delegate_iter {
111111
self.0.size_hint()
112112
}
113113
}
114-
}
114+
};
115+
(pattern reverse $te:ty : $ti:ty) => {
116+
#[stable(feature = "rust1", since = "1.0.0")]
117+
impl<'a, P: Pattern<'a>> Iterator for $ti
118+
where P::Searcher: ReverseSearcher<'a>
119+
{
120+
type Item = $te;
121+
122+
#[inline]
123+
fn next(&mut self) -> Option<$te> {
124+
self.0.next()
125+
}
126+
#[inline]
127+
fn size_hint(&self) -> (usize, Option<usize>) {
128+
self.0.size_hint()
129+
}
130+
}
131+
};
115132
}
116133

117134
/// A trait to abstract the idea of creating a new instance of a type from a
@@ -553,6 +570,19 @@ struct CharSplitsN<'a, P: Pattern<'a>> {
553570
invert: bool,
554571
}
555572

573+
/// An iterator over the substrings of a string, separated by a
574+
/// pattern, in reverse order.
575+
struct RCharSplits<'a, P: Pattern<'a>> {
576+
/// The slice remaining to be iterated
577+
start: usize,
578+
end: usize,
579+
matcher: P::Searcher,
580+
/// Whether an empty string at the end of iteration is allowed
581+
allow_final_empty: bool,
582+
finished: bool,
583+
}
584+
585+
556586
/// An iterator over the lines of a string, separated by `\n`.
557587
#[stable(feature = "rust1", since = "1.0.0")]
558588
pub struct Lines<'a> {
@@ -646,6 +676,43 @@ where P::Searcher: DoubleEndedSearcher<'a> {
646676
}
647677
}
648678

679+
impl<'a, P: Pattern<'a>> RCharSplits<'a, P> {
680+
#[inline]
681+
fn get_remainder(&mut self) -> Option<&'a str> {
682+
if !self.finished && (self.allow_final_empty || self.end - self.start > 0) {
683+
self.finished = true;
684+
unsafe {
685+
let string = self.matcher.haystack().slice_unchecked(self.start, self.end);
686+
Some(string)
687+
}
688+
} else {
689+
None
690+
}
691+
}
692+
}
693+
694+
#[stable(feature = "rust1", since = "1.0.0")]
695+
impl<'a, P: Pattern<'a>> Iterator for RCharSplits<'a, P>
696+
where P::Searcher: ReverseSearcher<'a>
697+
{
698+
type Item = &'a str;
699+
700+
#[inline]
701+
fn next(&mut self) -> Option<&'a str> {
702+
if self.finished { return None }
703+
704+
let haystack = self.matcher.haystack();
705+
match self.matcher.next_match_back() {
706+
Some((a, b)) => unsafe {
707+
let elt = haystack.slice_unchecked(b, self.end);
708+
self.end = a;
709+
Some(elt)
710+
},
711+
None => self.get_remainder(),
712+
}
713+
}
714+
}
715+
649716
/// The internal state of an iterator that searches for matches of a substring
650717
/// within a larger string using two-way search
651718
#[derive(Clone)]
@@ -1321,6 +1388,11 @@ delegate_iter!{pattern &'a str : SplitTerminator<'a, P>}
13211388
pub struct SplitN<'a, P: Pattern<'a>>(CharSplitsN<'a, P>);
13221389
delegate_iter!{pattern forward &'a str : SplitN<'a, P>}
13231390

1391+
/// Return type of `StrExt::rsplit`
1392+
#[stable(feature = "rust1", since = "1.0.0")]
1393+
pub struct RSplit<'a, P: Pattern<'a>>(RCharSplits<'a, P>);
1394+
delegate_iter!{pattern reverse &'a str : RSplit<'a, P>}
1395+
13241396
/// Return type of `StrExt::rsplitn`
13251397
#[stable(feature = "rust1", since = "1.0.0")]
13261398
pub struct RSplitN<'a, P: Pattern<'a>>(CharSplitsN<'a, P>);
@@ -1340,6 +1412,8 @@ pub trait StrExt {
13401412
fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P>;
13411413
fn splitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> SplitN<'a, P>;
13421414
fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P>;
1415+
fn rsplit<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplit<'a, P>
1416+
where P::Searcher: ReverseSearcher<'a>;
13431417
fn rsplitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> RSplitN<'a, P>;
13441418
fn match_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> MatchIndices<'a, P>;
13451419
#[allow(deprecated) /* for SplitStr */]
@@ -1436,6 +1510,19 @@ impl StrExt for str {
14361510
})
14371511
}
14381512

1513+
#[inline]
1514+
fn rsplit<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplit<'a, P>
1515+
where P::Searcher: ReverseSearcher<'a>
1516+
{
1517+
RSplit(RCharSplits {
1518+
start: 0,
1519+
end: self.len(),
1520+
matcher: pat.into_searcher(self),
1521+
allow_final_empty: true,
1522+
finished: false,
1523+
})
1524+
}
1525+
14391526
#[inline]
14401527
fn rsplitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> RSplitN<'a, P> {
14411528
RSplitN(CharSplitsN {

0 commit comments

Comments
 (0)