Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does `rev().rev()` work but `rev().skip(1).rev()` does not?

Tags:

rust

As title, in Rust, .rev().rev() works, .rev().skip(1) works, but .rev().skip(1).rev() does not. The following is the demonstration:

// This compiles
fn main() {
    let s = "Hello!";
    println!("{}", &s.chars().rev().skip(1).collect::<String>());
}
// This compiles
fn main() {
    let s = "Hello!";
    println!("{}", &s.chars().rev().rev().collect::<String>());
}
// This *does not* compile
fn main() {
    let s = "Hello!";
    println!("{}", &s.chars().rev().skip(1).rev().collect::<String>());
}

The last one does not compile:

error[E0277]: the trait bound `Chars<'_>: ExactSizeIterator` is not satisfied
 --> src/main.rs:3:45
  |
3 |     println!("{}", &s.chars().rev().skip(1).rev().collect::<String>());
  |                                             ^^^ the trait `ExactSizeIterator` is not implemented for `Chars<'_>`
  |
  = note: required because of the requirements on the impl of `ExactSizeIterator` for `Rev<Chars<'_>>`
  = note: required because of the requirements on the impl of `DoubleEndedIterator` for `Skip<Rev<Chars<'_>>>`

error[E0599]: the method `collect` exists for struct `Rev<Skip<Rev<Chars<'_>>>>`, but its trait bounds were not satisfied
  --> src/main.rs:3:51
   |
3  |        println!("{}", &s.chars().rev().skip(1).rev().collect::<String>());
   |                                                      ^^^^^^^ method cannot be called on `Rev<Skip<Rev<Chars<'_>>>>` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `Skip<Rev<Chars<'_>>>: DoubleEndedIterator`
           which is required by `Rev<Skip<Rev<Chars<'_>>>>: Iterator`
           `Rev<Skip<Rev<Chars<'_>>>>: Iterator`
           which is required by `&mut Rev<Skip<Rev<Chars<'_>>>>: Iterator`

Playground

Could someone explain why this is the case?

like image 238
HKTonyLee Avatar asked Oct 30 '21 22:10

HKTonyLee


People also ask

What does collect () do in Rust?

collect() can take all the values in an Iterator 's stream and stick them into a Vec . And the map method is now generating Result<i32, &str> values, so everything lines up.

How do you know if an iterator is empty in Rust?

You can make your iterator peekable and peek the first item; if it's None , then the iterator is empty.

What is iterator in Rust?

An iterator is responsible for the logic of iterating over each item and determining when the sequence has finished. When you use iterators, you don't have to reimplement that logic yourself. In Rust, iterators are lazy, meaning they have no effect until you call methods that consume the iterator to use it up.

Is it better to use Rev 1 or Rev 00?

Both way is OK ! I used to use Rev 1 for the original version of the document. But recently I moved to a new company where (they) we use Rev 00. Firstly I was surprised but later on I had different perspective: the newest edition has no revision at all, so Rev 0 sounds more logical. Version 1.0, 2.0, 3.0... Revision 00, 1.0, 2.0...

Is the original document Rev 1 or Rev 2?

My experience is the Original Document is Rev 1 and any changes to it would then become Rev 2. Is there a right way, or only what our system says? Both way is OK ! I used to use Rev 1 for the original version of the document. But recently I moved to a new company where (they) we use Rev 00.

Do you use Rev A as original or initial release?

We use Rev A as Original or Initial Release. They used to be IR but some thought it was Initial Release some thought In Revision. We adopted the same level structure as our Engineering drawings. Something people were already familiar with. Unless a customer or Regulatory Body requires it to be different.

Why use Rev as a freelancer?

Over 100,000 customers use Rev from industries like legal, media, education, and more, meaning there are always a variety of files available to work on. Rev provides a free online editor that makes it easy to listen to the file and type at the same time. As a freelancer, you’re your own boss and control your own schedule.


2 Answers

Calling .chars() returns an Iterator (Chars) which implements DoubleEndedIterator, which using .rev() requires.

fn rev(self) -> Rev<Self>
where
    Self: DoubleEndedIterator

Then calling .skip() produces a new Iterator (Skip), which only implements DoubleEndedIterator if the Iterator (in this case Chars) implements both DoubleEndedIterator and ExactSizeIterator.

impl<I> DoubleEndedIterator for Skip<I>
where
    I: DoubleEndedIterator + ExactSizeIterator

However, Chars does not implement ExactSizeIterator. So DoubleEndedIterator is not implemented for Skip. So now the requirement for calling .rev() is no longer uphold for the second call.

like image 174
vallentin Avatar answered Oct 26 '22 06:10

vallentin


The rev method returns a structure called Rev<I> where I is whatever iterator you called it on.

For example, my_str.chars() gets you a Chars struct which is an iterator. Calling .rev() gets you Rev<Chars>.

.rev() requires that I be a DoubleEndedIterator (which makes sense, reversing is done by just iterating from the back to the front).

The skip method returns a structure called Skip<I> where similarly, I is whatever iterator you called it on.

This is callable on any iterator (which makes sense).

However, Skip<I> only implements DoubleEndedIterator if I implements DoubleEndedIterator and ExactSizeIterator.

That means, that we can only call .rev on a Skip<I> if I: DoubleEndedIterator + ExactSizeIterator.

One last piece before we look at what you've written: Chars implements DoubleEndedIterator but not ExactSizeIterator since characters are variable length when encoded in utf8.

Taking a look at this whole thing:

s
    .chars() // DoubleEndedIterator
    .rev()   // DoubleEndedIterator
    .skip()  // Doesn't implement DoubleEndedIterator because it doesn't implement ExactSizeIterator
    .rev()   // Err, we need DoubleEndedIterator here.
like image 36
Optimistic Peach Avatar answered Oct 26 '22 06:10

Optimistic Peach