Kiwi breeder
Kiwi breeder

Reputation: 627

Why must a mutable reference be used in the for loop here?

This is probably a silly question, but this is kinda making me crazy. I have this simple code:

let args = env::args();
println!("num args read: {}", args.len());

for element in args {
   println!("{}", element);
}

let arg_string: String = args.collect();
 

This doesn't work and gives the error:

    |
4   |     let args = env::args();
    |         -------- move occurs because `args` has type `std::env::Args`, which does not implement the `Copy` trait
...
8   |     for element in args {
    |                    ----
    |                    |
    |                    `args` moved due to this implicit call to `.into_iter()`
    |                    help: consider borrowing to avoid moving into the for loop: `&args`
...
14  |     let arg_string: String = args.collect();
    |                              ^^^^ value used here after move

This makes sense as the into_iter() call consumes self. So I try to modify the for-loop to for element in &args and I get this error:

9 |     for element in &args {
  |                    -^^^^
  |                    |
  |                    `&std::env::Args` is not an iterator
  |                    help: consider removing the leading `&`-reference
  |
  = help: the trait `std::iter::Iterator` is not implemented for `&std::env::Args`
  = note: `std::iter::Iterator` is implemented for `&mut std::env::Args`, but not for `&std::env::Args`
  = note: required by `std::iter::IntoIterator::into_iter`

Why can't I iterate over a reference over a iterator? I found out that I can do

let mut args = env::args();
println!("num args read: {}", args.len());

for element in &mut args {
   println!("{}", element);
}

let arg_string: String = args.collect();

and this gets rid of the compiler error. Why must I take a mutable reference here instead of just a reference?

Upvotes: 3

Views: 346

Answers (1)

kmdreko
kmdreko

Reputation: 60692

You're using Args like it is a collection, but it isn't; its an iterator. Your &mut workaround satisfies the compiler but doesn't actually do what you want. The for-loop will exhaust the iterator meaning the args.collect() won't yield any values.

The fix is to call env::args() separately for each usage. You aren't saving anything by trying to use the same variable. I forgot that Args creates owned Strings, so you might be better off collecting the args into a Vec first if you need to iterate over it multiple times.

Upvotes: 3

Related Questions