Jak w Rust, jak utworzyć zmienny iterator? [duplikować]

Aug 16 2020

Mam problemy z okresami życia, gdy próbuję utworzyć mutowalny iterator w bezpiecznym Rdzy.

Oto, do czego zredukowałem swój problem:

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
}

Struktura mojego DataStructtestamentu nigdy się nie zmieni, ale muszę być w stanie zmutować zawartość przechowywanych w nim elementów. Na przykład,

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

Kompilator wyświetla błąd dotyczący sprzecznych czasów życia dla odniesienia, które próbuję zwrócić. Okres istnienia, którego się nie spodziewam, jest zakresem tej next(&mut self)funkcji.

Jeśli spróbuję dodać adnotację do czasu życia next(), zamiast tego kompilator mówi mi, że nie spełniłem cechy Iterator. Czy można to rozwiązać w bezpiecznej rdzy?

Oto błąd:

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`

edycje :

  • zmieniona implementacja next()tak, że kolejność iteracji jest permutacją oryginalnej sekwencji.

Odpowiedzi

3 PeterHall Aug 16 2020 at 15:06

Osoba sprawdzająca pożyczki nie jest w stanie udowodnić, że kolejne połączenia next()nie będą miały dostępu do tych samych danych. Powodem, dla którego jest to problem, jest to, że czas życia pożyczki jest równy czasowi życia iteratora, więc nie może udowodnić, że nie będzie dwóch zmiennych odwołań do tych samych danych w tym samym czasie.

Naprawdę nie ma sposobu na rozwiązanie tego problemu bez niebezpiecznego kodu - lub zmiany struktur danych. Możesz zrobić odpowiednik, slice::split_at_mutale biorąc pod uwagę, że nie możesz zmutować oryginalnych danych, i tak musiałbyś zaimplementować to w niebezpiecznym kodzie. Niebezpieczna implementacja może wyglądać mniej więcej tak:

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
        }
    }
}