Почему здесь в цикле for должна использоваться изменяемая ссылка?

#rust

#Ржавчина

Вопрос:

Возможно, это глупый вопрос, но это сводит меня с ума. У меня есть этот простой код:

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

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

let arg_string: String = args.collect();
 
  

Это не работает и выдает ошибку:

     |
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: `amp;args`
...
14  |     let arg_string: String = args.collect();
    |                              ^^^^ value used here after move
  

Это имеет смысл, поскольку into_iter() вызов потребляет self . Итак, я пытаюсь изменить цикл for на for element in amp;args и получаю эту ошибку:

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

Почему я не могу выполнить итерацию по ссылке через итератор? Я обнаружил, что могу сделать

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

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

let arg_string: String = args.collect();
  

и это избавляет от ошибки компилятора. Почему я должен использовать изменяемую ссылку здесь, а не просто ссылку?

Комментарии:

1. Вы получаете принадлежащие String s по требованию от итератора, но я на самом деле не знаю, почему их не может быть amp;'static str .

2. Сначала подумайте о создании Vec let args: Vec<_> = env::args().collect(); . Как упоминалось @kmdreko, env::args() возвращает итератор, который должен сохранять некоторые изменяемые внутренние указатели при выполнении цикла for.

3. FWIW, причиной (ну, одной из причин) Args является итератор вместо массива amp;'static str , потому что разные платформы имеют разные способы доступа к аргументам. Не все из них обязательно дают неизменяемые указатели на строки в кодировке UTF-8 в статической памяти. Args Интерфейс настолько общий, насколько это возможно.

4. @trentcl Я действительно думаю, что ни одна обычная ОС не гарантирует, что аргументы командной строки являются допустимыми UTF-8. В этом смысле Args это определенно не так обобщенно, как может быть, но ArgsOs есть.

Ответ №1:

Вы используете Args , как будто это коллекция, но это не так; это итератор. Ваш amp;mut обходной путь удовлетворяет компилятору, но на самом деле не делает то, что вы хотите. Цикл for исчерпает итератор, что означает args.collect() , что он не даст никаких значений.

Исправление заключается в вызове env::args() отдельно для каждого использования. Вы ничего не сохраняете, пытаясь использовать ту же переменную.Я забыл, что Args это создает принадлежащие String s, поэтому вам может быть лучше собрать аргументы в Vec первый, если вам нужно повторить его несколько раз.

Комментарии:

1. Я понимаю ваш комментарий, но я все еще не понимаю, почему я не могу выполнить цикл amp;args . В комментарии говорится, что args это итератор, но означает ли это, что вы не можете выполнять итерации по заимствованным итераторам?

2. В принципе. Итерация требует next() повторного вызова итератора, который изменяет его. Итак, чтобы выполнить итерацию с помощью заимствованного итератора, вам понадобится новый итератор. Вы могли бы получить новый итератор через clone() , но цикл for не сделает это за вас … и в данном случае это даже не вариант, потому Args что не реализует Clone .