web-dev-qa-db-fra.com

Comment écrire un itérateur qui renvoie des références à lui-même?

J'ai du mal à exprimer la durée de vie de la valeur de retour d'une implémentation Iterator. Comment puis-je compiler ce code sans changer la valeur de retour de l'itérateur? Je voudrais qu'il renvoie un vecteur de références.

Il est évident que je n'utilise pas correctement le paramètre de durée de vie, mais après avoir essayé différentes façons, je viens de renoncer, je ne sais pas quoi faire avec.

use std::iter::Iterator;

struct PermutationIterator<T> {
    vs: Vec<Vec<T>>,
    is: Vec<usize>,
}

impl<T> PermutationIterator<T> {
    fn new() -> PermutationIterator<T> {
        PermutationIterator {
            vs: vec![],
            is: vec![],
        }
    }

    fn add(&mut self, v: Vec<T>) {
        self.vs.Push(v);
        self.is.Push(0);
    }
}

impl<T> Iterator for PermutationIterator<T> {
    type Item = Vec<&'a T>;
    fn next(&mut self) -> Option<Vec<&T>> {
        'outer: loop {
            for i in 0..self.vs.len() {
                if self.is[i] >= self.vs[i].len() {
                    if i == 0 {
                        return None; // we are done
                    }
                    self.is[i] = 0;
                    self.is[i - 1] += 1;
                    continue 'outer;
                }
            }

            let mut result = vec![];

            for i in 0..self.vs.len() {
                let index = self.is[i];
                result.Push(self.vs[i].get(index).unwrap());
            }

            *self.is.last_mut().unwrap() += 1;

            return Some(result);
        }
    }
}

fn main() {
    let v1: Vec<_> = (1..3).collect();
    let v2: Vec<_> = (3..5).collect();
    let v3: Vec<_> = (1..6).collect();

    let mut i = PermutationIterator::new();
    i.add(v1);
    i.add(v2);
    i.add(v3);

    loop {
        match i.next() {
            Some(v) => {
                println!("{:?}", v);
            }
            None => {
                break;
            }
        }
    }
}

( Lien Playground )

error[E0261]: use of undeclared lifetime name `'a`
  --> src/main.rs:23:22
   |
23 |     type Item = Vec<&'a T>;
   |                      ^^ undeclared lifetime
27
elszben

Autant que je sache, vous voulez que l'itérateur retourne un vecteur de références en lui-même, non? Malheureusement, ce n'est pas possible à Rust.

Voici le trait Iterator réduit:

trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Item>;
}

Notez que il n'y a pas de connexion à vie entre &mut self Et Option<Item>. Cela signifie que la méthode next() ne peut pas renvoyer de références dans l'itérateur lui-même. Vous ne pouvez tout simplement pas exprimer une durée de vie des références renvoyées. C'est essentiellement la raison pour laquelle vous n'avez pas pu trouver un moyen de spécifier la durée de vie correcte - cela aurait ressemblé à ceci:

fn next<'a>(&'a mut self) -> Option<Vec<&'a T>>

sauf que ce n'est pas une méthode next() valide pour le trait Iterator.

Ces itérateurs (ceux qui peuvent renvoyer des références en eux-mêmes) sont appelés itérateurs de streaming. Vous pouvez trouver plus ici , ici et ici , si vous le souhaitez.

Mise à jour. Cependant, vous pouvez renvoyer une référence à une autre structure de votre itérateur - c'est ainsi que la plupart des itérateurs de collection fonctionnent. Cela pourrait ressembler à ceci:

pub struct PermutationIterator<'a, T> {
    vs: &'a [Vec<T>],
    is: Vec<usize>
}

impl<'a, T> Iterator for PermutationIterator<'a, T> {
    type Item = Vec<&'a T>;

    fn next(&mut self) -> Option<Vec<&'a T>> {
        ...
    }
}

Notez comment la durée de vie 'a Est désormais déclarée sur le bloc impl. C'est correct de le faire (obligatoire, en fait) car vous devez spécifier le paramètre de durée de vie sur la structure. Ensuite, vous pouvez utiliser le même 'a À la fois dans le type de retour Item et next(). Encore une fois, c'est ainsi que fonctionnent la plupart des itérateurs de collection.

24
Vladimir Matveev

@ réponse de VladimirMatveev est correct dans la façon dont il explique pourquoi votre code ne peut pas compiler. En un mot, il dit qu'un itérateur ne peut pas générer des valeurs empruntées de lui-même.

Cependant, il peut donner des valeurs empruntées à autre chose. C'est ce qui est réalisé avec Vec et Iter: le Vec possède les valeurs, et le Iter est juste un wrapper capable de fournir des références dans le Vec.

Voici un design qui réalise ce que vous voulez. L'itérateur est, comme avec Vec et Iter, juste un wrapper sur d'autres conteneurs qui possèdent réellement les valeurs.

use std::iter::Iterator;

struct PermutationIterator<'a, T: 'a> {
    vs : Vec<&'a [T]>,
    is : Vec<usize>
}

impl<'a, T> PermutationIterator<'a, T> {
    fn new() -> PermutationIterator<'a, T> { ... }

    fn add(&mut self, v : &'a [T]) { ... }
}

impl<'a, T> Iterator for PermutationIterator<'a, T> {
    type Item = Vec<&'a T>;
    fn next(&mut self) -> Option<Vec<&'a T>> { ... }
}

fn main() {
    let v1 : Vec<i32> = (1..3).collect();
    let v2 : Vec<i32> = (3..5).collect();
    let v3 : Vec<i32> = (1..6).collect();

    let mut i = PermutationIterator::new();
    i.add(&v1);
    i.add(&v2);
    i.add(&v3);

    loop {
        match i.next() {
            Some(v) => { println!("{:?}", v); }
            None => {break;}
        }
    }
}

(Aire de jeux)


Sans rapport avec votre problème initial. Si ce n'était que moi, je ferais en sorte que tous les vecteurs empruntés soient pris en même temps. L'idée est de supprimer les appels répétés à add et de passer directement tous les vecteurs empruntés à la construction:

use std::iter::{Iterator, repeat};

struct PermutationIterator<'a, T: 'a> {
    ...
}

impl<'a, T> PermutationIterator<'a, T> {
    fn new(vs: Vec<&'a [T]>) -> PermutationIterator<'a, T> {
        let n = vs.len();
        PermutationIterator {
            vs: vs,
            is: repeat(0).take(n).collect(),
        }
    }
}

impl<'a, T> Iterator for PermutationIterator<'a, T> {
    ...
}

fn main() {
    let v1 : Vec<i32> = (1..3).collect();
    let v2 : Vec<i32> = (3..5).collect();
    let v3 : Vec<i32> = (1..6).collect();
    let vall: Vec<&[i32]> = vec![&v1, &v2, &v3];

    let mut i = PermutationIterator::new(vall);
}

(Aire de jeux)

( [~ # ~] modifier [~ # ~] : modification de la conception de l'itérateur pour prendre un Vec<&'a [T]> Plutôt qu'un Vec<Vec<&'a T>>. Il est plus facile de prendre une référence au conteneur que de créer un conteneur de références.)

6
mdup

Comme mentionné dans d'autres réponses, cela s'appelle un itérateur de streaming et il nécessite des garanties différentes de Iterator de Rust. Une caisse qui fournit une telle fonctionnalité s'appelle à juste titre streaming-iterator et elle fournit le trait StreamingIterator .

Voici un exemple de mise en œuvre du trait:

extern crate streaming_iterator;

use streaming_iterator::StreamingIterator;

struct Demonstration {
    scores: Vec<i32>,
    position: usize,
}

// Since `StreamingIterator` requires that we be able to call
// `advance` before `get`, we have to start "before" the first
// element. We assume that there will never be the maximum number of
// entries in the `Vec`, so we use `usize::MAX` as our sentinel value.
impl Demonstration {
    fn new() -> Self {
        Demonstration {
            scores: vec![1, 2, 3],
            position: std::usize::MAX,
        }
    }

    fn reset(&mut self) {
        self.position = std::usize::MAX;
    }
}

impl StreamingIterator for Demonstration {
    type Item = i32;

    fn advance(&mut self) {
        self.position = self.position.wrapping_add(1);
    }

    fn get(&self) -> Option<&Self::Item> {
        self.scores.get(self.position)
    }
}

fn main() {
    let mut example = Demonstration::new();

    loop {
        example.advance();
        match example.get() {
            Some(v) => {
                println!("v: {}", v);
            }
            None => break,
        }
    }

    example.reset();

    loop {
        example.advance();
        match example.get() {
            Some(v) => {
                println!("v: {}", v);
            }
            None => break,
        }
    }
}

Malheureusement, les itérateurs de streaming seront limités jusqu'à ce que types génériques associés (GATs) de la RFC 1598 soient implémentés.

2
Shepmaster