Co oznacza błąd w tym przypadku:
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
Okazało się, że indeksowanie jest realizowana za pośrednictwem Index
a IndexMut
cechami i że v[1]
to cukier syntaktyczny dla *v.index(1)
. Mając tę wiedzę, próbowałem uruchomić następujący kod:
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;
}
Ku mojemu zaskoczeniu działa to bezbłędnie! Dlaczego pierwszy fragment nie działa, a drugi działa? Sposób, w jaki rozumiem dokumentację, powinien być równoważny, ale oczywiście tak nie jest.
rust
borrow-checker
Lucas Boucke
źródło
źródło
Odpowiedzi:
Wersja wypuszczona różni się nieco od tego, co masz. Linia
w rzeczywistości desugars do
Powoduje to ten sam komunikat o błędzie, ale adnotacje dają wskazówkę, co się dzieje:
Ważną różnicą w stosunku do twojej wersji testowej jest kolejność oceny. Argumenty wywołania funkcji są przetwarzane od lewej do prawej w podanej kolejności, przed wykonaniem wywołania funkcji. W tym przypadku oznacza to, że najpierw
&mut v
wycenia się, pożyczając w sposób zmiennyv
. NastępnieIndex::index(&v, 1)
należy ocenić, ale nie jest to możliwe -v
jest już pożyczone w sposób zmienny. Na koniec kompilator pokazuje, że zmienne odwołanie jest nadal potrzebne do wywołania funkcjiindex_mut()
, więc zmienne odwołanie jest nadal aktywne, gdy podejmowana jest próba udostępnienia odwołania.Wersja, która faktycznie się kompiluje, ma nieco inną kolejność oceny.
Po pierwsze, argumenty funkcji wywołań metod są oceniane od lewej do prawej, tzn. Są
*v.index(1)
oceniane jako pierwsze. Powoduje tousize
, a tymczasowe wspólne pożyczeniev
może zostać ponownie zwolnione. Następnie odbiorcaindex_mut()
jest oceniany, tj.v
Jest mutycznie pożyczany. Działa to dobrze, ponieważ wspólny kredyt został już sfinalizowany, a całe wyrażenie przechodzi przez moduł sprawdzający pożyczkę.Zauważ, że wersja, która się kompiluje, robi to tylko od czasu wprowadzenia „nie-leksykalnych żywotności”. We wcześniejszych wersjach Rust wspólna pożyczka istniałaby do końca wyrażenia i powodowała podobny błąd.
Moim zdaniem najczystszym rozwiązaniem jest użycie zmiennej tymczasowej:
źródło
*v.index_mut(*v.index_mut(1)) = 999;
porażka z „nie może pożyczyć v tak zmiennego więcej niż jeden raz” ~> nie powinna być kompilatorem, ponieważ jest w*v.index_mut(*v.index(1)) = 999;
stanie dowiedzieć się, że pożyczka wewnętrzna nie jest już potrzebna?*v.index(1)
jest wartość przechowywana w tym indeksie, a ta wartość nie wymaga utrzymania pożyczkiv
żywej. Z*v.index_mut(1)
drugiej strony wynik jest zmiennym wyrażeniem miejsca, które teoretycznie można przypisać, więc utrzymuje pożyczkę przy życiu. Na powierzchni powinno być możliwe nauczenie modułu sprawdzającego pożyczkę, że wyrażenie miejsca w kontekście wyrażenia wartości może być traktowane jako wyrażenie wartości, więc możliwe jest, że skompiluje się ono w niektórych przyszłych wersjach Rust.{ let index = *Index::index(&v, 1); let value = 999; *IndexMut::index_mut(&mut v, index) = value; }