Was bedeutet der Fehler in diesem Fall:
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
Ich fand , dass die Indizierung über die implementiert ist Index
und IndexMut
Züge und v[1]
ist syntaktischer Zucker für *v.index(1)
. Ausgestattet mit diesem Wissen habe ich versucht, den folgenden Code auszuführen:
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;
}
Zu meiner Überraschung funktioniert das einwandfrei! Warum funktioniert das erste Snippet nicht, das zweite jedoch? So wie ich die Dokumentation verstehe, sollten sie gleichwertig sein, aber dies ist offensichtlich nicht der Fall.
rust
borrow-checker
Lucas Boucke
quelle
quelle
Antworten:
Die Desugared-Version unterscheidet sich geringfügig von Ihrer Version. Die Linie
eigentlich Desugars zu
Dies führt zu derselben Fehlermeldung, aber die Anmerkungen geben einen Hinweis darauf, was passiert:
Der wichtige Unterschied zu Ihrer Desugared-Version ist die Bewertungsreihenfolge. Die Argumente eines Funktionsaufrufs werden von links nach rechts in der angegebenen Reihenfolge ausgewertet, bevor der Funktionsaufruf tatsächlich ausgeführt wird. In diesem Fall bedeutet dies, dass zunächst
&mut v
eine stimmbare Ausleihe ausgewertet wirdv
. Als nächstesIndex::index(&v, 1)
sollte ausgewertet werden, aber dies ist nicht möglich -v
ist bereits veränderlich ausgeliehen. Schließlich zeigt der Compiler, dass die veränderbare Referenz für den Funktionsaufruf noch benötigt wirdindex_mut()
, sodass die veränderbare Referenz noch aktiv ist, wenn versucht wird, die gemeinsam genutzte Referenz aufzurufen .Die tatsächlich kompilierte Version hat eine etwas andere Auswertungsreihenfolge.
Zunächst werden die Funktionsargumente für die Methodenaufrufe von links nach rechts
*v.index(1)
ausgewertet , dh zuerst ausgewertet. Dies führt zu einemusize
, und das temporäre gemeinsame Ausleihen vonv
kann wieder freigegeben werden. Dann wird der Empfänger vonindex_mut()
ausgewertet, dhv
veränderlich ausgeliehen. Dies funktioniert einwandfrei, da der gemeinsame Kredit bereits abgeschlossen wurde und der gesamte Ausdruck den Kreditprüfer besteht.Beachten Sie, dass die kompilierte Version dies erst seit Einführung der "nicht-lexikalischen Lebensdauer" tut. In früheren Versionen von Rust blieb die gemeinsame Ausleihe bis zum Ende des Ausdrucks bestehen und führte zu einem ähnlichen Fehler.
Die meiner Meinung nach sauberste Lösung ist die Verwendung einer temporären Variablen:
quelle
*v.index_mut(*v.index_mut(1)) = 999;
Fehler mit "kann v nicht mehr als einmal als veränderbar ausleihen" fehlschlägt ~> sollte der Compiler nicht sein, da*v.index_mut(*v.index(1)) = 999;
er herausfinden kann, dass das innere Ausleihen nicht mehr benötigt wird?*v.index(1)
ist der Wert , der in diesem Index gespeichert ist, und dieser Wert erfordert nicht, dass die Ausleihe amv
Leben bleibt . Das Ergebnis von*v.index_mut(1)
ist andererseits ein veränderlicher Ortsausdruck , der theoretisch zugeordnet werden könnte, so dass der Kredit am Leben bleibt. Oberflächlich betrachtet sollte es möglich sein, dem Ausleihprüfer beizubringen, dass ein Ortsausdruck im Wertausdruckskontext als Wertausdruck behandelt werden kann, sodass dies möglicherweise in einer zukünftigen Version von Rust kompiliert wird.{ let index = *Index::index(&v, 1); let value = 999; *IndexMut::index_mut(&mut v, index) = value; }