Nurbol Alpysbayev
Nurbol Alpysbayev

Reputation: 21871

How do I get the index from the beginning in a reversed iterator of chars of a string?

This code:

let s = String::from("hi");

for (idx, ch) in s.chars().rev().enumerate() {
    println!("{} {}", idx, ch);
}

prints

0 i
1 h

but I want to know the real index, so that it would print:

1 i
0 h

What's the best way to do that? Currently I only think of first getting .count() and subtracting each idx from it, but maybe there's a better method that I overlooked.

Upvotes: 4

Views: 1007

Answers (1)

edwardw
edwardw

Reputation: 13942

This is complicated, as they say. If your string is ASCII only, you can do the obvious enumeration then reverse against a String's byte iterator:

fn main() {
    let s = String::from("hi");

    for (idx, ch) in s.bytes().enumerate().rev() {
        println!("{} {}", idx, ch as char);
    }
}

This doesn't work for Unicode strings in general because of what a char in Rust stands for:

The char type represents a single character. More specifically, since 'character' isn't a well-defined concept in Unicode, char is a 'Unicode scalar value', which is similar to, but not the same as, a 'Unicode code point'.

This can be illustrated by the following:

fn main() {
    let s = String::from("y̆");

    println!("{}", s.len());
    for (idx, ch) in s.bytes().enumerate() {
        println!("{} {}", idx, ch);
    }

    for (idx, ch) in s.chars().enumerate() {
        println!("{} {}", idx, ch);
    }
}

This weird looking string has length of 3, as in 3 u8s. At the same time it has 2 chars. So ExactSizeIterator can't be trivially implemented for std::str::Chars, but it can and does be implemented for std::str::Bytes. This is significant because to reverse a given iterator, it has to be DoubleEndedIterator:

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

But DoubleEndedIterator is only available for enumeration iterator if the underlying iterator is also ExactSizeIterator:

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

In conclusion, you can only do s.bytes().enumerate().rev(), but not s.chars().enumerate().rev(). If you absolutely have to index the enumerated char iterator of a String that way, you are on your own.

Upvotes: 5

Related Questions