¿Cómo escribo un iterador que devuelve referencias a sí mismo?

Resuelto elszben asked hace 9 años • 4 respuestas

Tengo problemas para expresar la vida útil del valor de retorno de una Iteratorimplementación. ¿Cómo puedo compilar este código sin cambiar el valor de retorno del iterador? Me gustaría que devuelva un vector de referencias.

Es obvio que no estoy usando correctamente el parámetro de vida útil, pero después de probar varias formas simplemente me di por vencido y no tengo idea de qué hacer con él.

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

( Enlace al patio de juegos )

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

Hasta donde tengo entendido, lo que quieres es que el iterador devuelva un vector de referencias a sí mismo, ¿verdad? Desafortunadamente, no es posible en Rust.

Este es el rasgo recortado Iterator:

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

Tenga en cuenta que no existe una conexión de por vida entre &mut selfy Option<Item>. Esto significa que ese next()método no puede devolver referencias al propio iterador. Simplemente no se puede expresar la vida útil de las referencias devueltas. Esta es básicamente la razón por la que no pudiste encontrar una manera de especificar la vida útil correcta; se habría visto así:

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

excepto que este no es un next()método válido para Iteratorel rasgo.

Estos iteradores (los que pueden devolver referencias a sí mismos) se denominan iteradores de transmisión . Puedes encontrar más aquí , aquí y aquí , si quieres.

Actualizar. Sin embargo, puede devolver una referencia a alguna otra estructura desde su iterador; así es como funcionan la mayoría de los iteradores de colecciones. Podría verse así:

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

Observe cómo la vida útil 'aahora se declara en implbloque. Está bien hacerlo (de hecho, es obligatorio) porque necesita especificar el parámetro de vida útil en la estructura. Entonces puedes usar el mismo tipo de devolución 'atanto en Itemcomo en next(). Nuevamente, así es como funcionan la mayoría de los iteradores de colecciones.

Vladimir Matveev avatar May 24 '2015 10:05 Vladimir Matveev

La respuesta de @VladimirMatveev es correcta en cuanto a cómo explica por qué su código no se puede compilar. En pocas palabras, dice que un iterador no puede generar valores prestados desde sí mismo.

Sin embargo, puede generar valores prestados de otra cosa. Esto es lo que se logra con Vecand Iter: the Vecposee los valores y the Iteres solo un contenedor capaz de generar referencias dentro de Vec.

Aquí tienes un diseño que logra lo que deseas. El iterador es, como con Vecy Iter, solo un contenedor sobre otros contenedores que realmente poseen los valores.

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

(Patio de juegos)


No relacionado con su problema inicial. Si fuera solo yo, me aseguraría de que todos los vectores prestados se tomen a la vez. La idea es eliminar las llamadas repetidas addy pasar directamente todos los vectores prestados en la construcción:

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

(Patio de juegos)

( EDITAR : se cambió el diseño del iterador para tomar a Vec<&'a [T]>en lugar de a Vec<Vec<&'a T>>. Es más fácil llevar una referencia al contenedor que construir un contenedor de referencias).

mdup avatar May 24 '2015 11:05 mdup