lry
lry

Reputation: 309

Using mutable or immutable reference in a string chars for-loop

I'm confused how a mutable reference works in a for-loop while an immutable reference does not. Is it an iterator?

I found that after loop, the reference chars refers to value "".

No reference

fn main() {
    let str_value: String = "abc".to_string();

    let chars = str_value.chars();
    for char_value in chars {
        println!("char: {}", char_value);
    }
}
char: a
char: b
char: c

Immutable reference

fn main() {
    let str_value: String = "abc".to_string();

    let chars = str_value.chars();
    for char_value in &chars {
        println!("char: {}", char_value);
    }
}
error[E0277]: `&std::str::Chars<'_>` is not an iterator
 --> src/main.rs:5:23
  |
5 |     for char_value in &chars {
  |                       -^^^^^
  |                       |
  |                       `&std::str::Chars<'_>` is not an iterator
  |                       help: consider removing the leading `&`-reference
  |
  = help: the trait `std::iter::Iterator` is not implemented for `&std::str::Chars<'_>`
  = note: `std::iter::Iterator` is implemented for `&mut std::str::Chars<'_>`, but not for `&std::str::Chars<'_>`
  = note: required by `std::iter::IntoIterator::into_iter`

Mutable reference

fn main() {
    let str_value: String = "abc".to_string();

    let mut chars = str_value.chars();
    for char_value in &mut chars {
        println!("char: {}", char_value);
    }

    // why chars equal ""?
    assert_eq!(chars.as_str(), "");
}
char: a
char: b
char: c

Upvotes: 2

Views: 1664

Answers (1)

phimuemue
phimuemue

Reputation: 36031

chars is of type Chars which implements ("is a") iterator whose elements are of type char.

In Rust, you can use for-loops on things that implement ("are a") iterator.

Thus, your first example is covered by this: Iterates over chars.

The second example does not work because &chars has type &Chars (a borrowed, immutable reference), which does not implement Iterator.

In the third example, however, you have &mut Chars, and this is an iterator:

impl<I: Iterator + ?Sized> Iterator for &mut I {
    type Item = I::Item;
    fn next(&mut self) -> Option<I::Item> { (**self).next() }
    // some details omitted
}

The above says: For any type I that is an Iterator and that may not satisfy ?Sized (all this holds for the type Chars), the type &mut I is an Iterator, whose iterated values are the same as the original iterated types (type Item = I::Item), and that delegates next (and some other methods) to the original iterator.

Thus, the third example sees &mut Chars, knows that Chars is an Iterator, and infers that then &mut Chars is also an Iterator that can be used in a for-loop.

as_str -- according to its documentation (see the example there) gives you the remaining (i.e. not yet iterated) substring, so after the iteration, it should just say "" (i.e. empty), since there is nothing left to be iterated over.

Upvotes: 5

Related Questions