Don Rowe
Don Rowe

Reputation: 883

Getting Enumerate to work as ExactSizeIterator in Rust

I want to use Rust's Enumerate to get both a character and its index in the slice from each iteration:

fn main() {
    for (j, val) in "dummy string".chars().enumerate().rev() {
        // ...
    }
}

When I compile with cargo run I get:

error: the trait `core::iter::ExactSizeIterator` is not implemented for the type `core::str::Chars<'_>` [E0277]
    for (j, val) in "dummy string".chars().enumerate().rev() {
                                                       ^~~
help: see the detailed explanation for E0277

error: the trait `core::iter::ExactSizeIterator` is not implemented for the type `core::str::Chars<'_>` [E0277]
    for (j, val) in "dummy string".chars().enumerate().rev() {
        // ...
    }

I can understand why this would fail: the rev method needs an ExactSizeIterator since it needs to know the last element in the slice and its index from the beginning. Is it possible to get an ExactSizeIterator in this case, or does the length of the iterator need to be baked in at compile time? If it is possible, is it just a matter of specifying the iterator with something like as ExactSizeIterator or something like that?

Upvotes: 0

Views: 1717

Answers (1)

Shepmaster
Shepmaster

Reputation: 431679

The docs for ExactSizeIterator state:

An iterator that knows its exact length.

Many Iterators don't know how many times they will iterate, but some do. If an iterator knows how many times it can iterate, providing access to that information can be useful. For example, if you want to iterate backwards, a good start is to know where the end is.

But that's not the actual trait required by rev!

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

The ExactSizeIterator requirement comes from Enumerate's implementation of DoubleEndedIterator:

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

Is it possible to get an ExactSizeIterator in this case, or does the length of the iterator need to be baked in at compile time?

The Chars iterator needs to support both ExactSizeIterator and DoubleEndedIterator, but it only natively supports DoubleEndedIterator.

In order to implement ExactSizeIterator for Chars, you'd need to be able to look at an arbitrary string and know (in a small enough time) how many characters it is made of. This is not generally possible with the UTF-8 encoding, the only encoding of Rust strings.

The length of the iterator is never a compile-time constant.

is it just a matter of specifying the iterator with something like as ExactSizeIterator

You cannot make a type into something it is not.

If you really need this, you could collect it all into a big Vec:

fn main() {
    let chars: Vec<_> = "dummy string".chars().collect();
    for (j, val) in chars.into_iter().enumerate().rev() {
        println!("{}, {}", j, val)
    }
}

It's also possible you actually want the characters in reverse order with the count in increasing direction:

fn main() {
    for (j, val) in "dummy string".chars().rev().enumerate() {
        println!("{}, {}", j, val)
    }
}

But you said this:

a character and its index in the slice

Since strings are UTF-8, it's possible you mean you want the number of bytes into the slice. That can be found with the char_indices iterator:

fn main() {
    for (j, val) in "dummy string".char_indices().rev() {
        println!("{}, {}", j, val)
    }
}

Upvotes: 6

Related Questions