web-dev-qa-db-fra.com

Que signifie «ne peut pas emprunter comme immuable parce qu'il est également emprunté comme mutable» signifie dans un index de tableau imbriqué?

Que signifie l'erreur dans ce cas:

fn main() {
    let mut v: Vec<usize> = vec![1, 2, 3, 4, 5];
    v[v[1]] = 999;
}
error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
 --> src/main.rs:3:7
  |
3 |     v[v[1]] = 999;
  |     --^----
  |     | |
  |     | immutable borrow occurs here
  |     mutable borrow occurs here
  |     mutable borrow later used here

J'ai trouvé que l'indexation est implémentée via les traits Index et IndexMut et que v[1] Est du sucre syntaxique pour *v.index(1). Muni de ces connaissances, j'ai essayé d'exécuter le code suivant:

use std::ops::{Index, IndexMut};

fn main() {
    let mut v: Vec<usize> = vec![1, 2, 3, 4, 5];
    *v.index_mut(*v.index(1)) = 999;
}

À ma grande surprise, cela fonctionne parfaitement! Pourquoi le premier extrait ne fonctionne-t-il pas, alors que le second fonctionne? D'après ma compréhension de la documentation, ils devraient être équivalents, mais ce n'est évidemment pas le cas.

16
Lucas Boucke

La version désucrée est légèrement différente de ce que vous avez. La ligne

v[v[1]] = 999;

fait desugars à

*IndexMut::index_mut(&mut v, *Index::index(&v, 1)) = 999;

Il en résulte le même message d'erreur, mais les annotations donnent une indication sur ce qui se passe:

error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
 --> src/main.rs:7:48
  |
7 |     *IndexMut::index_mut(&mut v, *Index::index(&v, 1)) = 999;
  |      ------------------- ------                ^^ immutable borrow occurs here
  |      |                   |
  |      |                   mutable borrow occurs here
  |      mutable borrow later used by call

La différence importante avec votre version désucrée est l'ordre d'évaluation. Les arguments d'un appel de fonction sont évalués de gauche à droite dans l'ordre indiqué, avant d'effectuer réellement l'appel de fonction. Dans ce cas, cela signifie que le premier &mut v Est évalué, empruntant mutuellement v. Ensuite, Index::index(&v, 1) doit être évaluée, mais ce n'est pas possible - v est déjà emprunté de manière mutuelle. enfin, le compilateur montre que la référence mutable est toujours nécessaire pour l'appel de fonction à index_mut(), de sorte que la référence mutable est toujours active lorsque la référence partagée est tentée.

La version qui compile a un ordre d'évaluation légèrement différent.

*v.index_mut(*v.index(1)) = 999;

Tout d'abord, les arguments de fonction des appels de méthode sont évalués de gauche à droite, c'est-à-dire que *v.index(1) est évalué en premier. Il en résulte un usize et l'emprunt partagé temporaire de v peut être à nouveau libéré. Ensuite, le récepteur de index_mut() est évalué, c'est-à-dire que v est mutuellement emprunté. Cela fonctionne très bien, car l'emprunt partagé a déjà été finalisé et l'expression entière passe le vérificateur d'emprunt.

Notez que la version qui compile ne le fait que depuis l'introduction des "durées de vie non lexicales". Dans les versions antérieures de Rust, l'emprunt partagé durerait jusqu'à la fin de l'expression et entraînerait une erreur similaire.

La solution la plus propre à mon avis est d'utiliser une variable temporaire:

let i = v[1];
v[i] = 999;
16
Sven Marnach