Rust заимствует странное поведение

Aug 22 2020

Как и код ржавчины ниже: whileцикл компилируется и работает нормально, но for iterверсия не компилируется из-за ошибки:

error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
  --> src/main.rs:22:9
   |
20 |     for i in v.iter() {
   |              --------
   |              |
   |              immutable borrow occurs here
   |              immutable borrow later used here
21 |         println!("v[i]: {}", i);
22 |         v.push(20);
   |         ^^^^^^^^^^ mutable borrow occurs here

error: aborting due to previous error

Но, как я понял, whileцикл также имеет тот же сценарий, lenа getтакже имеет неизменное заимствование, почему он не конфликтует с pushкак заимствовать изменчиво? Пожалуйста, посоветуйте, чего мне здесь не хватает, большое спасибо за просветление!

fn main() {
    let mut v = Vec::new();
    
    
    v.push(1);
    v.push(2);
    v.push(3);
    v.push(4);
    
    let mut i = 0;
    
    while i < v.len() && i < 10 {
        
        v.push(20);
        println!("v[i]: {:?}", v.get(i));
        i += 1;
    }
    
    // for i in v.iter() {
    //     println!("v[i]: {}", i);
    //     v.push(20);
        
    // }
    
}

Ответы

3 Herohtar Aug 21 2020 at 23:31

forВерсия кода примерно эквивалентно следующему:

fn main() {
    let mut v = Vec::new();
    v.push(1);
    v.push(2);
    v.push(3);
    v.push(4);
    
    let mut it = v.iter();
    while let Some(i) = it.next() {
        println!("v[i]: {}", i);
        v.push(20);
    }
}

Игровая площадка

Если вы попытаетесь скомпилировать это, вы получите ошибку, которая может иметь немного больше смысла:

error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
  --> src/main.rs:11:9
   |
8  |     let mut it = v.iter();
   |                  - immutable borrow occurs here
9  |     while let Some(i) = it.next() {
   |                         -- immutable borrow later used here
10 |         println!("v[i]: {}", i);
11 |         v.push(20);
   |         ^^^^^^^^^^ mutable borrow occurs here

Итератор vнеизменно заимствует заимствования на протяжении всего цикла, поэтому вы не можете принимать какие-либо изменяемые заимствования внутри цикла.

Конечно, даже если бы вы могли это сделать, у вас получился бы бесконечный цикл, потому что вы продолжали добавлять еще один элемент.

1 BlackBeans Aug 21 2020 at 23:21

Проще говоря, когда вы звоните .iter(), вы создаете новые объекты (итератор), который заимствует свой вектор (непреложно), и дает элемент один за другим, что означает , что вы на самом деле брать vв целом время цикла . С другой стороны, когда вы обращаетесь к нему через, .get(i)вы напрямую заимствуете один элемент за раз из вектора, и поэтому он освобождается от ограничений заимствования, когда вы push.

Причина такого ограничения очень проста: представьте, что ваш реальный forцикл скомпилирован, он будет работать вечно (и чтобы предотвратить это в whileцикле, вам пришлось добавить искусственное условие i<10!), Тогда как это явно не намеченная цель (или если было бы очевидно, что вы бы сделали это иначе, например, с помощью оператора while letили loop), и Rust пытается предотвратить вас «выстрелить себе в ногу», потому что вы на самом деле не знаете, как делать то, что хотите, и пытаетесь сделать не то путь.

Чтобы делать то, что вы хотели, вы могли:

for i in 0..v.len() {
    v.push(20)
}

потому что v.len()вызов не заимствует vвремя всего forцикла, а только в начале.