2014-09-06 8 views
9

wpadłem na problem, który upraszcza się, co następuje:Czy mogę napisać Iterator, który sam się mutuje, a następnie zwraca się do siebie?

struct MyIter { 
    vec: Vec<i8>, 
} 

fn fill_with_useful_data(v: &mut Vec<i8>) { 
    /* ... */ 
} 

impl<'a> Iterator for MyIter { 
    type Item = &'a [i8]; 

    fn next(&mut self) -> Option<&'a [i8]> { 
     fill_with_useful_data(&mut self.vec); 

     Some(&self.vec) 
    } 
} 

fn main() { 
    for slice in (MyIter { vec: Vec::new() }) { 
     println!("{}", slice); 
    } 
} 

ten generuje błąd:

error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates 
--> src/main.rs:9:6 
    | 
9 | impl<'a> Iterator for MyIter { 
    |  ^^ unconstrained lifetime parameter 

Chodzi o to, że iterator robi kilka prac, które odzwierciedla w swoich dziedzinach i na każdy krok, daje w sobie odniesienie do kodu wywołującego. W tym przypadku mógłbym go modelować jako dostarczający kopię stanu zamiast odniesienia, ale udawajmy, że nie jest to możliwe lub po prostu niewygodne.

Intuicyjnie to nie powinno być problemu, ponieważ kontroler pożyczyć może zapewnić, że .next() nie nazywa się ponownie, gdy uzyskano odniesienia może być nadal stosowany do kontroli stanu iterator, ale cecha Iterator nie wydaje się przewidywać tego rodzaju rzeczy bezpośrednio. Nawet z pewnymi permutacjami, jak tylko utrzymywanie odniesienia do wektora w samym iteratorze lub uczynienie iteratora odniesieniem lub czymś, co spowoduje, że te wcielenia zostaną wcześniej wypieczone, nie mogę uzyskać niczego poza kontrolą wypożyczenia.

Przeczytałem na blogu "Iterators yielding mutable references", ale nie jestem pewien, czy/jak to dotyczy mojego problemu, który nie dotyczy zmiennych referencji.

Odpowiedz

10

To nie jest możliwe. Jeśli było to dozwolone, można ponownie wywołać next iw ten sposób zmodyfikować dane, które są również widoczne przez & lub nawet całkowicie unieważnić odniesienie. Dzieje się tak dlatego, że nie ma żadnego związku między obiektem self a zwróconym odwołaniem: nie istnieje jednoznaczne łączenie ich w życie.

dla kompilatora rozumować o tym i umożliwić powrót odniesienie do self obok potrzebuje podpisu jak

fn next(&'a mut self) -> Option<&'a [i8]> 

Jednak ten różni się od podpisania cechy co jest niedozwolone w postaci kodu generycznego, że właśnie trwa T: Iterator<...> nie może stwierdzić, że istnieją różne wymagania dotyczące użycia wartości zwracanej dla niektórych T; wszystko musi być traktowane identycznie.

Cecha Iterator jest zaprojektowana dla wartości zwracanych, które są niezależne od obiektu iteratora, co jest niezbędne dla adapterów iteratora, takich jak .collect, aby były poprawne i bezpieczne. Jest to bardziej restrykcyjne niż to konieczne dla wielu zastosowań (np. Przejściowe zastosowanie wewnątrz pętli for), ale tak właśnie jest w tej chwili. Nie sądzę, że mamy teraz odpowiednie narzędzia do ogólnego ujednolicenia tej cechy/pętli for (szczególnie, myślę, że potrzebujemy powiązanych typów z wyższymi czasami rangowania), ale może w przyszłości.

+0

W porządku, właśnie przestałem implementować 'Iterator' i stworzyłem własną pętlę for z makrem. Nie myślałem o tym, jak ".collect()" nie wyszło. – ben

+0

Jest to również przydatne w przypadku przejazdów, w których nie można użyć pętli 'for': while while Some (element) = iter.next() {...' – bluss

+1

Dla przyszłego odniesienia, ten typ iteratora ('fn next (& 'a self) ') jest często określany jako * iterator przesyłania strumieniowego *. Niektóre przykłady dyskusji [1] (https://users.rust-lang.org/t/returning-borrowed-values-from-an-iterator/1096), [2] (https://www.reddit.com/r/rust/comments/2t1rxx/more_general_iterator_trait /) i [skrzynia implementująca jego formę] (https://github.com/sfackler/streaming-iterator). – Shepmaster

Powiązane problemy