Karat meminjam perilaku aneh

Aug 22 2020

Seperti kode karat di bawah ini: whileloop mengkompilasi dan berjalan dengan baik, tetapi for iterversi tidak dapat dikompilasi, karena kesalahan:

error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
  --> src/main.rs:22:9
   |
20 |     for i in v.iter() {
   |              --------
   |              |
   |              immutable borrow occurs here
   |              immutable borrow later used here
21 |         println!("v[i]: {}", i);
22 |         v.push(20);
   |         ^^^^^^^^^^ mutable borrow occurs here

error: aborting due to previous error

Namun sebagaimana dipahami, whileloop juga memiliki skenario yang sama, lendan getjuga meminjam secara tetap, mengapa tidak bertentangan dengan pushsebagai pinjam bersama? Mohon saran apa yang hilang dari pemahaman saya di sini, terima kasih banyak atas pencerahannya!

fn main() {
    let mut v = Vec::new();
    
    
    v.push(1);
    v.push(2);
    v.push(3);
    v.push(4);
    
    let mut i = 0;
    
    while i < v.len() && i < 10 {
        
        v.push(20);
        println!("v[i]: {:?}", v.get(i));
        i += 1;
    }
    
    // for i in v.iter() {
    //     println!("v[i]: {}", i);
    //     v.push(20);
        
    // }
    
}

Jawaban

3 Herohtar Aug 21 2020 at 23:31

The forversi kode Anda kira-kira sama dengan yang berikut:

fn main() {
    let mut v = Vec::new();
    v.push(1);
    v.push(2);
    v.push(3);
    v.push(4);
    
    let mut it = v.iter();
    while let Some(i) = it.next() {
        println!("v[i]: {}", i);
        v.push(20);
    }
}

Tempat bermain

Jika Anda mencoba mengompilasi, Anda akan mendapatkan error yang mungkin lebih masuk akal:

error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
  --> src/main.rs:11:9
   |
8  |     let mut it = v.iter();
   |                  - immutable borrow occurs here
9  |     while let Some(i) = it.next() {
   |                         -- immutable borrow later used here
10 |         println!("v[i]: {}", i);
11 |         v.push(20);
   |         ^^^^^^^^^^ mutable borrow occurs here

Iterator meminjam secara vpermanen selama seluruh durasi loop, sehingga Anda tidak dapat mengambil pinjaman yang bisa berubah di dalam loop.

Tentu saja, bahkan jika Anda bisa melakukan itu, Anda akan berakhir dengan loop tak terbatas karena Anda terus menambahkan item lain.

1 BlackBeans Aug 21 2020 at 23:21

Sederhananya, ketika Anda menelepon .iter(), Anda membuat objek baru (iterator), yang meminjam vektor Anda (immutably), dan memberikan elemen satu per satu, yang berarti Anda benar-benar meminjam vpada sepanjang waktu dari loop . Di sisi lain, ketika Anda mengaksesnya melalui .get(i)Anda langsung meminjam satu elemen pada saat itu dari vektor, sehingga dibebaskan dari batasan peminjaman saat Anda push.

Alasan pembatasan tersebut sangat sederhana: bayangkan forloop Anda yang sebenarnya telah dikompilasi, itu akan berjalan selamanya (dan untuk mencegah hal ini dalam whileloop, Anda harus menambahkan kondisi buatan i<10!), Sedangkan ini jelas bukan tujuan yang dimaksudkan (atau jika itu akan menjadi Anda jelas akan melakukannya sebaliknya, misalnya dengan pernyataan while letatau loop), dan Rust mencoba untuk mencegah Anda "menembak diri sendiri di kaki" karena Anda tidak benar-benar bagaimana melakukan apa yang ingin Anda lakukan, dan mencoba yang salah cara.

Untuk melakukan apa yang ingin Anda lakukan, Anda dapat melakukan:

for i in 0..v.len() {
    v.push(20)
}

karena v.len()panggilan tidak meminjam vuntuk seluruh waktu forloop, tetapi hanya di awal.