La ruggine prende in prestito un comportamento strano

Aug 22 2020

Come il codice ruggine qui sotto: il whileciclo si compila e funziona bene, ma la for iterversione non si compila, a causa di un errore:

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

Ma come inteso, whileanche il ciclo ha lo stesso scenario lene getprende in prestito immutabilmente, perché non è in conflitto con pushil prestito in modo mutevole? Per favore, avvisa qual è la mia comprensione che manca qui, grazie mille per l'illuminazione!

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);
        
    // }
    
}

Risposte

3 Herohtar Aug 21 2020 at 23:31

La forversione del codice è più o meno equivalente alla seguente:

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);
    }
}

Terreno di gioco

Se provi a compilarlo, otterrai un errore che forse ha un po 'più senso:

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

L'iteratore prende in prestito vimmutabilmente per l'intera durata del ciclo, quindi non è possibile prendere in prestito mutabili all'interno del ciclo.

Ovviamente, anche se potessi farlo, finiresti con un ciclo infinito perché continui ad aggiungere un altro elemento.

1 BlackBeans Aug 21 2020 at 23:21

In poche parole, quando chiami .iter(), crei un nuovo oggetto (un iteratore), che prende in prestito il tuo vettore (immutabilmente), e dà gli elementi uno per uno, il che significa che in realtà prendi in prestito vl' intero tempo del ciclo . D'altra parte, quando si accede tramite .get(i)si prende in prestito direttamente un elemento alla volta dal vettore, e quindi è liberato dalle restrizioni di prestito quando si push.

La ragione di tale restrizione è molto semplice: immagina che il tuo forciclo effettivo sia stato compilato, verrebbe eseguito per sempre (e per evitare questo nel whileciclo, devi aggiungere la condizione artificiale i<10!), Mentre questo chiaramente non è l'obiettivo prefissato (o se saresti chiaramente che lo faresti altrimenti, ad esempio con una dichiarazione while leto loop), e Rust cerca di impedirti di "spararti a una gamba" perché non sai davvero come fare quello che vuoi fare, e provi il torto modo.

Per fare quello che volevi fare, potresti fare:

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

perché la v.len()chiamata non prende vin prestito per il tempo dell'intero forciclo, ma solo all'inizio.