Apa artinya kesalahan dalam hal ini:
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
Saya menemukan bahwa pengindeksan diimplementasikan melalui Index
dan IndexMut
sifat-sifat dan bahwa v[1]
adalah sintaksis gula untuk *v.index(1)
. Dilengkapi dengan pengetahuan ini, saya mencoba menjalankan kode berikut:
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;
}
Yang mengejutkan saya, ini bekerja dengan sempurna! Mengapa potongan pertama tidak berfungsi, tetapi yang kedua tidak? Cara saya memahami dokumentasi, mereka harus setara, tetapi ini jelas tidak terjadi.
rust
borrow-checker
Lucas Boucke
sumber
sumber
Jawaban:
Versi yang diinginkan sedikit berbeda dari yang Anda miliki. Garis
sebenarnya keinginan untuk
Ini menghasilkan pesan kesalahan yang sama, tetapi anotasi memberikan petunjuk tentang apa yang terjadi:
Perbedaan penting dengan versi yang Anda inginkan adalah urutan evaluasi. Argumen panggilan fungsi dievaluasi dari kiri ke kanan dalam urutan yang tercantum, sebelum benar-benar membuat panggilan fungsi. Dalam hal ini ini berarti bahwa pertama
&mut v
dievaluasi, saling meminjamv
. Selanjutnya,Index::index(&v, 1)
harus dievaluasi, tetapi ini tidak mungkin -v
sudah dipinjam secara gonta-ganti. Akhirnya, kompiler menunjukkan bahwa referensi yang dapat diubah masih diperlukan untuk pemanggilan fungsiindex_mut()
, sehingga referensi yang dapat diubah masih hidup ketika referensi bersama dicoba.Versi yang sebenarnya mengkompilasi memiliki urutan evaluasi yang sedikit berbeda.
Pertama, argumen fungsi untuk panggilan metode dievaluasi dari kiri ke kanan, yaitu
*v.index(1)
dievaluasi terlebih dahulu. Ini menghasilkanusize
, dan pinjaman sementara sementarav
dapat dirilis kembali. Kemudian, penerimaindex_mut()
dievaluasi, yaituv
dipinjam bersama. Ini berfungsi dengan baik, karena pinjaman bersama telah diselesaikan, dan seluruh ekspresi melewati pemeriksa pinjaman.Perhatikan bahwa versi yang mengkompilasi hanya melakukannya sejak diperkenalkannya "masa hidup non-leksikal". Dalam versi Rust yang lebih lama, pinjaman bersama akan hidup sampai akhir ekspresi dan menghasilkan kesalahan yang sama.
Solusi terbersih menurut saya adalah menggunakan variabel sementara:
sumber
*v.index_mut(*v.index_mut(1)) = 999;
gagal dengan "tidak bisa meminjam v sebagai bisa berubah lebih dari sekali" ~> seharusnya tidak menjadi kompiler, karena dengan*v.index_mut(*v.index(1)) = 999;
dapat mengetahui bahwa pinjaman batin tidak lagi diperlukan?*v.index(1)
adalah nilai yang disimpan di indeks itu, dan nilai itu tidak perlu untuk menjaga pinjamanv
tetap hidup. Hasil dari*v.index_mut(1)
, di sisi lain, adalah ekspresi tempat yang bisa berubah yang secara teori dapat ditugaskan, sehingga tetap membuat pinjaman tetap hidup. Di permukaan, harus mungkin untuk mengajarkan pemeriksa pinjaman bahwa ekspresi tempat dalam konteks ekspresi nilai dapat diperlakukan sebagai ekspresi nilai, jadi mungkin ini akan dikompilasi di beberapa versi Rust yang akan datang.{ let index = *Index::index(&v, 1); let value = 999; *IndexMut::index_mut(&mut v, index) = value; }