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
Je trouve que l' indexation est mis en œuvre par les Index
et IndexMut
traits et 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.
rust
borrow-checker
Lucas Boucke
la source
la source
Réponses:
La version désucrée est légèrement différente de ce que vous avez. La ligne
fait desugars à
Il en résulte le même message d'erreur, mais les annotations donnent une indication sur ce qui se passe:
La différence importante par rapport à 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é, en empruntant mutuellementv
. Ensuite,Index::index(&v, 1)
devrait être évalué, mais ce n'est pas possible -v
est déjà mutuellement emprunté. 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.
Tout d'abord, les arguments de fonction des appels de méthode sont évalués de gauche à droite, c'est
*v.index(1)
-à- dire qu'ils sont évalués en premier. Il en résulte unusize
et l'emprunt partagé temporaire dev
peut être à nouveau libéré. Ensuite, le récepteur deindex_mut()
est évalué, c'estv
-à- dire qu'il 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:
la source
*v.index_mut(*v.index_mut(1)) = 999;
échoue avec "ne peut pas emprunter v en tant que mutable plus d'une fois" ~> le compilateur ne devrait-il pas l'être, comme avec la*v.index_mut(*v.index(1)) = 999;
possibilité de comprendre que l'emprunt interne n'est plus nécessaire?*v.index(1)
est la valeur stockée à cet index, et cette valeur ne nécessite pas de conserver l'emprunt dev
vivant. Le résultat de*v.index_mut(1)
, d'autre part, est une expression de lieu mutable qui pourrait théoriquement être assignée à, donc elle maintient l'emprunt en vie. En surface, il devrait être possible d'enseigner au vérificateur d'emprunt qu'une expression de lieu dans un contexte d'expression de valeur peut être traitée comme une expression de valeur, il est donc possible que cela soit compilé dans une future version de Rust.{ let index = *Index::index(&v, 1); let value = 999; *IndexMut::index_mut(&mut v, index) = value; }