Di Rust, bagaimana cara membuat iterator yang bisa berubah? [duplikat]

Aug 16 2020

Saya mengalami kesulitan dengan masa pakai saat mencoba membuat iterator yang bisa berubah di Rust yang aman.

Inilah yang saya telah mereduksi masalah saya menjadi:

struct DataStruct<T> {
    inner: Box<[T]>,
}

pub struct IterMut<'a, T> {
    obj: &'a mut DataStruct<T>,
    cursor: usize,
}

impl<T> DataStruct<T> {
    fn iter_mut(&mut self) -> IterMut<T> {
        IterMut { obj: self, cursor: 0 }
    }
}

impl<'a, T> Iterator for IterMut<'a, T> {
    type Item = &'a mut T;

    fn next(&mut self) -> Option<Self::Item> {
        let i = f(self.cursor);
        self.cursor += 1;
        self.obj.inner.get_mut(i)
    }
}

fn f(i: usize) -> usize {
   // some permutation of i
}

Struktur DataStructkeinginan saya tidak akan pernah berubah, tetapi saya harus dapat mengubah konten elemen yang disimpan di dalamnya. Sebagai contoh,

let mut ds = DataStruct{ inner: vec![1,2,3].into_boxed_slice() };
for x in ds {
  *x += 1;
}

Kompiler memberi saya kesalahan tentang masa pakai yang bertentangan untuk referensi yang saya coba kembalikan. Seumur hidup yang ditemukannya yang tidak saya harapkan adalah cakupan next(&mut self)fungsinya.

Jika saya mencoba memberi anotasi seumur hidup pada next(), maka kompiler, sebaliknya, memberi tahu saya bahwa saya belum memenuhi sifat Iterator. Apakah ini dapat dipecahkan dalam karat yang aman?

Inilah kesalahannya:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/iter_mut.rs:25:24
   |
25 |         self.obj.inner.get_mut(i)
   |                        ^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 22:5...
  --> src/iter_mut.rs:22:5
   |
22 | /     fn next(&mut self) -> Option<Self::Item> {
23 | |         let i = self.cursor;
24 | |         self.cursor += 1;
25 | |         self.obj.inner.get_mut(i)
26 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/iter_mut.rs:25:9
   |
25 |         self.obj.inner.get_mut(i)
   |         ^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 19:6...
  --> src/iter_mut.rs:19:6
   |
19 | impl<'a, T> Iterator for IterMut<'a, T> {
   |      ^^
note: ...so that the types are compatible
  --> src/iter_mut.rs:22:46
   |
22 |       fn next(&mut self) -> Option<Self::Item> {
   |  ______________________________________________^
23 | |         let i = self.cursor;
24 | |         self.cursor += 1;
25 | |         self.obj.inner.get_mut(i)
26 | |     }
   | |_____^
   = note: expected  `std::iter::Iterator`
              found  `std::iter::Iterator`

edit :

  • implementasi yang diubah next()sehingga urutan iterasi adalah permutasi dari urutan asli.

Jawaban

3 PeterHall Aug 16 2020 at 15:06

Pemeriksa peminjam tidak dapat membuktikan bahwa panggilan berikutnya ke next()tidak akan mengakses data yang sama. Alasan mengapa ini menjadi masalah adalah karena masa pinjaman adalah selama masa pakai iterator, sehingga tidak dapat membuktikan bahwa tidak akan ada dua referensi yang dapat berubah ke data yang sama pada waktu yang sama.

Sebenarnya tidak ada cara untuk menyelesaikan ini tanpa kode yang tidak aman - atau mengubah struktur data Anda. Anda dapat melakukan persamaan dari slice::split_at_muttetapi, mengingat Anda tidak dapat mengubah data asli, Anda tetap harus menerapkannya dalam kode yang tidak aman. Penerapan yang tidak aman bisa terlihat seperti ini:

impl<'a, T> Iterator for IterMut<'a, T> {
    type Item = &'a mut T;

    fn next(&mut self) -> Option<Self::Item> {
        let i = self.cursor;
        self.cursor += 1;
        if i < self.obj.inner.len() {
            let ptr = self.obj.inner.as_mut_ptr();
            unsafe {
                Some(&mut *ptr.add(i))
            }
        } else {
            None
        }
    }
}