Mengapa beberapa referensi yang bisa berubah membuat tidak mungkin untuk menetapkan anggota struct untuk dirinya sendiri? [duplikat]

Aug 17 2020

Saya tidak dapat meminjam di mana saya pikir saya bisa. Saya mengurangi masalah menjadi kasus ini:

struct A<'a> {
    borrow: &'a mut u8,
}

fn does_nothing<'b, 'c>(a: &'b mut A<'c>) {
    a.borrow = a.borrow;
}
error[E0623]: lifetime mismatch
 --> src/lib.rs:6:16
  |
5 | fn does_nothing<'b, 'c>(a: &'b mut A<'c>) {
  |                            -------------
  |                            |
  |                            these two types are declared with different lifetimes...
6 |     a.borrow = a.borrow;
  |                ^^^^^^^^ ...but data from `a` flows into `a` here

Tampaknya a.borrowmemiliki persimpangan 'bdan 'cdan karena itu tidak dapat dijamin masih memiliki seumur hidup 'c.

Saya tidak memiliki masalah nyata dengan ini dan dapat mengatasinya dengan membuat kedua masa hidup sama, tetapi mengapa ini tidak meminjam cek?


Tampaknya struct tidak penting untuk menunjukkan masalah ini dan pinjaman ganda menunjukkannya dengan mudah.

Saya memiliki tiga fungsi yang sangat mirip, tetapi saya akan kesulitan mengetahui mana yang dapat dikompilasi dan kesalahan mana yang akan diberikan oleh yang tidak dikompilasi.

Fungsi umum sederhana:

fn only_borrow<T>(a: &mut T) {
    *a = *a;
}

menghasilkan kesalahan:

error[E0507]: cannot move out of `*a` which is behind a mutable reference
 --> src/lib.rs:2:10
  |
2 |     *a = *a;
  |          ^^ move occurs because `*a` has type `T`, which does not implement the `Copy` trait

Memasukkan tingkat tipuan ekstra mengubah kesalahan

fn only_borrow_double<T>(a: &mut &mut T) {
    *a = *a;
}
error[E0623]: lifetime mismatch
 --> src/lib.rs:2:10
  |
1 | fn only_borrow_double<T>(a: &mut &mut T) {
  |                             -----------
  |                             |
  |                             these two types are declared with different lifetimes...
2 |     *a = *a;
  |          ^^ ...but data from `a` flows into `a` here

Mengubah masa pakai yang tersirat dapat memperbaiki kesalahan:

fn working_double<'b, T>(a: &'b mut &'b mut T) {
    *a = *a;
}

Jawaban

Emoun Aug 18 2020 at 09:59

Anda harus melihat masa hidup Anda 'bdan 'c:

  • &'b mut ...berarti Anda memiliki referensi yang aktif dan valid untuk suatu "waktu" 'b.
  • A<'c>berarti Anda memiliki objek yang hidup dan valid 'c.

Apa yang tidak Anda miliki adalah hubungan khusus antara dua masa kehidupan ini. Satu-satunya hal yang dapat disimpulkan oleh kompilator adalah karena A<'c>berada di belakang a &'b, 'cmust outlive 'b, yaitu, kapan pun 'bvalid, begitu juga 'c. Padahal, yang terpenting, bukan sebaliknya.

Seperti yang Anda tunjukkan, kompilator membutuhkan 'bdan 'cseumur hidup yang sama. Kenapa ini?

Mari kita lihat kemungkinan kita:

  1. 'cdan 'btidak memiliki hubungan: Sangat mudah untuk melihat bahwa tanpa hubungan apapun, kompilator tidak dapat menjamin apapun tentang apa yang dimasukkan A.borrowdan dengan demikian tidak akan mengizinkannya.
  2. 'cbenar-benar hidup lebih lama 'b, yaitu, beberapa tempat 'cvalid 'btidak:
    a.borrow = a.borrowmenjadi peminjaman kembali amenggunakan 'bseumur hidup. Jawaban ini menjelaskan mengapa hal itu terjadi . Namun, ini berarti bahwa asekarang tergantung pada masa 'bhidup, yang tidak berlaku untuk beberapa waktu ayang sah (karena amemiliki masa hidup 'c). Ini memberikan kesalahan.
  3. 'bbenar-benar hidup lebih lama 'c: Jika kita memiliki hubungan ini, itu mungkin berhasil. Peminjaman kembali akan valid, karena kita mendapatkan masa hidup yang "lebih besar" ( 'b) daripada yang kita minta ( 'c). Namun, kami telah 'c: 'bmenyimpulkan oleh kompilator di balik layar. Oleh karena itu, menambahkan masa hidup ini berarti kedua masa kehidupan menjadi sama dan kita kemudian kembali ke tempat kita memulai:
struct A<'a> {
    borrow: &'a mut u8,
}

/// We add the relation 'b: 'c
fn does_nothing<'b: 'c, 'c>(a: &'b mut A<'c>) {
    a.borrow = a.borrow;
}