2015-02-28 2 views
5

Je voudrais utiliser Peekable comme base pour une nouvelle opération cautious_take_while qui agit comme take_while de IteratorExt mais sans consommer le premier élément défaillant. (Il y a une question secondaire à savoir si c'est une bonne idée, et s'il y a de meilleures façons d'atteindre cet objectif dans Rust - je serais heureux pour des conseils dans ce sens, mais surtout j'essaie de comprendre où mon code est rupture).La mise en œuvre d'un « prudent » take_while utilisant Peekable

L'API que je suis en train de permettre est essentiellement:

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") 

J'ai pris une fissure à creating a MCVE here, mais je me fais:

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

Pour autant que je peux dire , Je suis le même modèle que TakeWhile de Rust en termes de mes signatures de fonction, mais je vois un comportement différent du vérificateur d'emprunt. Quelqu'un peut-il signaler ce que je fais mal?

Répondre

5

La chose drôle avec by_ref() est qu'il renvoie une référence mutable à lui-même:

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

Cela fonctionne parce que le trait Iterator est mis en œuvre pour le pointeur mutable à Iterator Type. Intelligent!

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

La fonction take_while fonctionne norme car il utilise le trait Iterator, qui est automatiquement résolu à &mut Peekable<T>.

Mais votre code ne fonctionne pas car Peekable est une structure, pas un trait, donc votre CautiousTakeWhileable doit spécifier le type, et vous essayez de le prendre en charge, mais vous ne pouvez pas, car vous avez un pointeur mutable. Solution, ne pas prendre un Peekable<T> mais &mut Peekable<T>. Vous devez spécifier la durée de vie trop:

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

Un effet secondaire curieux de cette solution est que maintenant by_ref n'est pas nécessaire, parce que cautious_take_while() prend une référence mutable, il ne vole pas la propriété. L'appel by_ref() est nécessaire pour take_while() car il peut prendre soit Peekable<T> ou &mut Peekable<T>, et il prend par défaut le premier. Avec l'appel by_ref() il résoudra à la seconde.

Et maintenant que je le comprends enfin, je pense que ce pourrait être une bonne idée de changer la définition de struct CautiousTakeWhile pour inclure le bit "peekable" dans la structure elle-même. La difficulté est que la durée de vie doit être spécifiée manuellement, si j'ai raison.Quelque chose comme:

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

et le reste est plus ou moins simple.

+0

Merci @rodrigo! J'ai incorporé votre première suggestion pour produire un exemple de travail à http://is.gd/NalTYL. Mais quand j'essaie d'introduire le typage dans la structure, comme dans http://is.gd/6c64vf, j'obtiens 'error: le trait * core :: clone :: Clone * n'est pas implémenté pour le type * & mut core: : iter :: Peekable * ', que je n'arrive pas à surmonter en ajoutant' + Clone' aux limites des traits à la ligne 43. – Bosh

+0

@Bosh. Pas sûr, mais je pense qu'un pointeur mutable ne peut pas être cloné. Votre variante accepte le 'Clone' probablement parce que' Peekable' implémente 'Clone' explicitement. Peut-être que vous pouvez faire la même chose, mais le code aurait besoin d'un refactoring ... – rodrigo

+1

Great. J'ai arrêté de dériver de Clone et de nettoyer les timelines: http://is.gd/ljjJAE. Merci encore pour votre aide + explications! – Bosh

1

C'était difficile! Je vais mener avec la viande du code, puis tenter de l'expliquer (si je comprends bien ...). C'est aussi la version laide, non gélifiée, car je voulais réduire la complexité accessoire.

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

En fait, Rodrigo seems to have a good explanation, donc je vais céder la parole à ce que, à moins que vous voulez me expliquer quelque chose de spécifique.