Why can I not reverse the result of str::split?

旧街凉风 提交于 2020-07-03 07:06:08

问题


According to the docs for Split, there is a rev method on the result of doing split on a string:

fn main() {
    let mut length = 0;
    let mut mult = 1;
    for part in "1:30".split(":").rev() {
        length += mult * part.parse::<i32>().unwrap();
        mult *= 60;
    }
}

I get the following error:

error[E0277]: the trait bound `std::str::pattern::StrSearcher<'_, '_>: std::str::pattern::DoubleEndedSearcher<'_>` is not satisfied
 --> src/main.rs:4:35
  |
4 |     for part in "1:30".split(":").rev() {
  |                                   ^^^ the trait `std::str::pattern::DoubleEndedSearcher<'_>` is not implemented for `std::str::pattern::StrSearcher<'_, '_>`
  |
  = note: required because of the requirements on the impl of `std::iter::DoubleEndedIterator` for `std::str::Split<'_, &str>`

error[E0277]: the trait bound `std::str::pattern::StrSearcher<'_, '_>: std::str::pattern::DoubleEndedSearcher<'_>` is not satisfied
 --> src/main.rs:4:17
  |
4 |     for part in "1:30".split(":").rev() {
  |                 ^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::str::pattern::DoubleEndedSearcher<'_>` is not implemented for `std::str::pattern::StrSearcher<'_, '_>`
  |
  = note: required because of the requirements on the impl of `std::iter::DoubleEndedIterator` for `std::str::Split<'_, &str>`
  = note: required because of the requirements on the impl of `std::iter::Iterator` for `std::iter::Rev<std::str::Split<'_, &str>>`

回答1:


The problem is that rev() is defined on the Split iterator only if it implements DoubleEndedIterator, but Split only implements DoubleEndedIterator if the searcher of the pattern you are splitting on satisfies DoubleEndedSearcher:

impl<'a, P> DoubleEndedIterator for Split<'a, P>
where
    P: Pattern<'a>,
    <P as Pattern<'a>>::Searcher: DoubleEndedSearcher<'a>, 

The documentation lists which types implement DoubleEndedSearcher. None of the types there correspond to &str pattern, so you can't use rev() when you split on string.

In your particular case, I guess, it is sufficient to change split(":") to split(':') (i.e. split on character instead of string) because the character pattern searcher does implement DoubleEndedSearcher.

Such features of Rust (conditional trait implementation and trait bounds local to a method) allow writing really expressive code, but they can be sometimes hard to read through.




回答2:


The other answers are correct, but I want to point out rsplit. This is probably more obvious and more performant.

So, why can't you use rev? As other answers state, it's not implemented for StrSearcher. But why is it not implemented? From the DoubleEndedSearcher docs:

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".

The problem with reversing the iterator using strings is this:

"baaab".split("aa") // -> ["b", "aa", "ab"];

However, if you were to start at the end of the string, you'd get something like:

"baaab".split("aa").rev() // -> ["b", "aa", "ba"]

Which is clearly not the same set of items in a different order!

Simply put, you can't reverse an iterator that is split on strings because there's no efficient way of knowing when the next result is. You'd have to split the entire string into a collection, then reverse the collection!

This is why rsplit exists - it means start at the end of the string and split to the beginning, in an efficient manner.




回答3:


TLDR: StrSearcher (the type that does searching for string patterns) doesn't implement DoubleEndedSearcher and, as such, the split iterator doesn't implement DoubleEndedIterator and, as such, you can't call rev on it.

If you look at the documentation on that page for rev, you'll see where Self: DoubleEndedIterator. This means that rev is defined if and only if the type the Iterator trait is being implemented for (which is Split) also has an implementation of the DoubleEndedIterator trait.

If you look further down, you'll see:

impl<'a, P> DoubleEndedIterator for Split<'a, P>
where P: Pattern<'a>, P::Searcher: DoubleEndedSearcher<'a>

Thus, DoubleEndedIterator is implemented for Split if and only if both of those conditions are satisfied: P must be a Pattern and the Searcher type it defines must implement DoubleEndedSearcher.

Now, you're using a string literal as the pattern, so if you check the documentation for the str type, you'll see:

impl<'a, 'b> Pattern<'a> for &'b str

Within that, the associated Searcher type is defined as:

type Searcher = StrSearcher<'a, 'b>

Nearly there! Follow the link to the documentation for StrSearcher and...

...there's no implementation of DoubleEndedSearcher. Thus, the required bounds are not satisfied and rev can't be used on the Split iterator.



来源:https://stackoverflow.com/questions/29901415/why-can-i-not-reverse-the-result-of-strsplit

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!