Rust emprunter un comportement étrange

Aug 22 2020

Comme le code rust ci-dessous: la whileboucle se compile et fonctionne correctement, mais la for iterversion ne compile pas, en raison d'une erreur:

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

Mais comme compris, la whileboucle a également le même scénario, lenet getemprunte également immuablement, pourquoi elle n'est pas en conflit avec pushcomme emprunt mutuellement? Veuillez indiquer ce que ma compréhension manque ici, merci beaucoup pour l'illumination!

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

Réponses

3 Herohtar Aug 21 2020 at 23:31

La forversion de votre code est à peu près équivalente à la suivante:

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

Terrain de jeux

Si vous essayez de compiler cela, vous obtiendrez une erreur qui aura peut-être un peu plus de sens:

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'itérateur emprunte vimmuablement pendant toute la durée de la boucle, vous ne pouvez donc prendre aucun emprunt modifiable dans la boucle.

Bien sûr, même si vous pouviez le faire, vous vous retrouveriez avec une boucle infinie car vous continuez à ajouter un autre élément.

1 BlackBeans Aug 21 2020 at 23:21

Autrement dit, lorsque vous appelez .iter(), vous créez un nouveau objets (iterator), qui emprunte votre vecteur (immuablement), et donne l'un des éléments par un, ce qui signifie que vous empruntez en fait vle tout le temps de la boucle . En revanche, lorsque vous y accédez via .get(i)vous empruntez directement un élément à la fois du vecteur, et ainsi il est libéré des restrictions d'emprunt lorsque vous push.

La raison d'une telle restriction est très simple: imaginez que votre forboucle réelle ait été compilée, elle fonctionnerait pour toujours (et pour éviter cela dans la whileboucle, vous deviez ajouter la condition artificielle i<10!), Alors que ce n'est clairement pas l'objectif visé ce serait clairement que vous le feriez autrement, par exemple avec une déclaration while letou loop), et Rust essaie de vous empêcher de vous «tirer une balle dans la jambe» parce que vous ne savez pas vraiment comment faire ce que vous voulez faire, et essayez de vous tromper façon.

Pour faire ce que vous vouliez faire, vous pourriez faire:

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

car l' v.len()appel n'emprunte pas vpour le temps de la forboucle entière , mais seulement au début.