2015-02-28 19 views
5

Chciałbym użyć Peekable jako podstawę dla nowego cautious_take_while operacji, która działa jak take_while od IteratorExt ale bez spożywania pierwszej nieudanej element. (Istnieje boczne pytanie, czy jest to dobry pomysł i czy istnieją lepsze sposoby osiągnięcia tego celu w Rust - byłbym szczęśliwy z powodu wskazówek w tym kierunku, ale głównie staram się zrozumieć, gdzie jest mój kod łamanie).Wdrażanie „ostrożny” take_while użyciu Peekable

API Próbuję włączyć to w zasadzie:

let mut chars = "abcdefg.".chars().peekable(); 

let abc : String = chars.by_ref().cautious_take_while(|&x| x != 'd'); 
let defg : String = chars.by_ref().cautious_take_while(|&x| x != '.'); 

// yielding (abc = "abc", defg = "defg") 

Wziąłem pęknięcia na creating a MCVE here, ale ja dostaję:

:10:5: 10:19 error: cannot move out of borrowed content :10 chars.by_ref().cautious_take_while(|&x| x != '.');

O ile mogę powiedzieć Podążam za tym samym wzorem co Rusta TakeWhile pod względem moich sygnatur funkcji, ale widzę różne zachowanie od kontrolera pożyczek. Czy ktoś może wskazać, co robię źle?

Odpowiedz

5

Najśmieszniejsze jest to, że z by_ref() zwraca zmienny odniesienie do siebie:

pub trait IteratorExt: Iterator + Sized { 
    fn by_ref(&mut self) -> &mut Self { self } 
} 

To działa, ponieważ cecha Iterator jest realizowany za zmienny wskaźnik do iterator typu. Mądry!

impl<'a, I> Iterator for &'a mut I where I: Iterator, I: ?Sized { ... } 

Standardowa take_while funkcja działa, ponieważ używa cecha Iterator, który jest automatycznie rozwiązane do &mut Peekable<T>.

Ale twój kod nie działa, ponieważ Peekable jest strukturą, a nie cechą, więc twój CautiousTakeWhileable musi określać typ i próbujesz przejąć na niego własność, ale nie możesz, ponieważ masz zmienny wskaźnik.

Rozwiązanie, nie należy przyjmować Peekable<T>, ale &mut Peekable<T>. trzeba będzie określić żywotność też:

impl <'a, T: Iterator, P> Iterator for CautiousTakeWhile<&'a mut Peekable<T>, P> 
where P: FnMut(&T::Item) -> bool { 
    //... 
} 

impl <'a, T: Iterator> CautiousTakeWhileable for &'a mut Peekable<T> { 
    fn cautious_take_while<P>(self, f: P) -> CautiousTakeWhile<&'a mut Peekable<T>, P> 
    where P: FnMut(&T::Item) -> bool { 
     CautiousTakeWhile{inner: self, condition: f,} 
    } 
} 

Ciekawym efektem ubocznym tego rozwiązania jest to, że teraz by_ref nie jest potrzebna, ponieważ cautious_take_while() zajmuje zmienny odniesienia, więc nie kraść własność. Wywołanie by_ref() jest wymagane dla take_while(), ponieważ może to być Peekable<T> lub &mut Peekable<T>, a domyślnie jest to pierwsze. Po wywołaniu by_ref() rozwiąże to drugie.

A teraz, kiedy w końcu to rozumiem, myślę, że może być dobrym pomysłem zmienić definicję struct CautiousTakeWhile w celu włączenia bitu peekable do samej struktury. Trudność polega na tym, że czas życia musi być określony ręcznie, jeśli mam rację.Coś jak:

struct CautiousTakeWhile<'a, T: Iterator + 'a, P> 
    where T::Item : 'a { 
    inner: &'a mut Peekable<T>, 
    condition: P, 
} 
trait CautiousTakeWhileable<'a, T>: Iterator { 
    fn cautious_take_while<P>(self, P) -> CautiousTakeWhile<'a, T, P> where 
     P: FnMut(&Self::Item) -> bool; 
} 

a reszta jest mniej lub bardziej prosta.

+0

Dzięki @rodrigo! Włączyłem twoją pierwszą sugestię, aby stworzyć przykład pracy pod adresem http://is.gd/NalTYL. Ale kiedy próbuję wprowadzić piszę do struktury, jak w http://is.gd/6c64vf, otrzymuję komunikat 'error: the trait * core :: clone :: Clone * nie jest zaimplementowany dla jądra typu * i mut: : iter :: Peekable * ', którego nie mogę pokonać poprzez dodanie' + Clone' do granic cech w linii 43. – Bosh

+0

@Bosh. Nie jestem pewien, ale myślę, że zmiennego wskaźnika nie można sklonować. Twój wariant akceptuje 'Clone' prawdopodobnie dlatego, że' Peekable' insynuuje '' Clone' jawnie. Może możesz zrobić to samo, ale kod będzie wymagał refaktoryzacji ... – rodrigo

+1

Świetnie. Przestałem czerpać z 'Clone' i czyściłem linie czasu: http://is.gd/ljjJAE. Jeszcze raz dziękuję za pomoc + wyjaśnienia! – Bosh

1

To było trudne! Poprowadzę z kawałkiem kodu, a potem spróbuję to wyjaśnić (jeśli to rozumiem ...). Jest to również brzydka, nieudokumentowana wersja, ponieważ chciałem zmniejszyć przypadkową złożoność.

use std::iter::Peekable; 

fn main() { 
    let mut chars = "abcdefg.".chars().peekable(); 

    let abc: String = CautiousTakeWhile{inner: chars.by_ref(), condition: |&x| x != 'd'}.collect(); 
    let defg: String = CautiousTakeWhile{inner: chars.by_ref(), condition: |&x| x != '.'}.collect(); 
    println!("{}, {}", abc, defg); 
} 

struct CautiousTakeWhile<'a, I, P> //' 
    where I::Item: 'a, //' 
      I: Iterator + 'a, //' 
      P: FnMut(&I::Item) -> bool, 
{ 
    inner: &'a mut Peekable<I>, //' 
    condition: P, 
} 

impl<'a, I, P> Iterator for CautiousTakeWhile<'a, I, P> 
    where I::Item: 'a, //' 
      I: Iterator + 'a, //' 
      P: FnMut(&I::Item) -> bool 
{ 
    type Item = I::Item; 

    fn next(&mut self) -> Option<I::Item> { 
     let return_next = 
      match self.inner.peek() { 
       Some(ref v) => (self.condition)(v), 
       _ => false, 
      }; 
     if return_next { self.inner.next() } else { None } 
    } 
} 

Właściwie Rodrigo seems to have a good explanation, więc będę odroczyć do tego, chyba że chcesz mi wyjaśnić coś konkretnego.