Soy un novato de Rust y estoy tratando de descubrir cuál es la mejor manera de hacer lo siguiente en Rust:

struct ThingIterator {
    current: String,
    stop: String,
}

impl Iterator for ThingIterator {
    type Item = &str;
    fn next(&mut self) -> Option<&str> {
        if self.current == self.stop {
            return None;
        }
        // For testing
        self.current = self.stop;
        Some(&self.current)
    }
}

fn main() {
    let pi = ThingIterator {
        current: String::from("Ask"),
        stop: String::from("Zoo"),
    };
    println!("Number of things={}", pi.count());
}

Mi error es:

error[E0106]: missing lifetime specifier
 --> src/main.rs:7:17
  |
7 |     type Item = &str;
  |                 ^ expected lifetime parameter

error: aborting due to previous error

Esto tiene sentido, necesito especificar cuánto tiempo va a ser válida la referencia devuelta por next (). Supongo que la función en sí está bien, ya que el tiempo de vida se pierde (no estoy seguro acerca de la conjugación de elission), pero de alguna manera necesito definir el tiempo de vida de la fila "type Item = & str".

En mi caso, será válido siempre que "actual" sea válido, es decir, la misma vida útil que "self".

No he visto nada en el libro de Rust u otra documentación que me ayude a resolver este caso.

PD Lo siento si estoy descifrando la nomenclatura, soy muy nuevo en Rust. Gracias

2
Christian Svensson 29 dic. 2019 a las 13:32

2 respuestas

La mejor respuesta

La duración en &mut self en next no está dentro del alcance cuando se define el tipo Item, por lo que Item no puede depender de esa duración. En cambio, es típico que ThingIterator contenga referencias en lugar de datos de propiedad. Si todavía hay una estructura que posee datos, probablemente implementará IntoIterator para que &OwnsData se convierta en el tipo que utiliza referencias.

// ThingIterator is now generic in the lifetime 'a
// and it holds references rather than owned Strings.
struct ThingIterator<'a> {
    current: &'a str,
    stop: &'a str,
}

impl<'a> Iterator for ThingIterator<'a> {
    // Now we can use the lifetime from ThingIterator here.
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        if self.current == self.stop {
            return None;
        }
        // For testing
        self.current = self.stop;
        Some(self.current)
    }
}

// Typically, you'll have a type that owns its data
// Like Vec<T>, HashSet<T>, etc.
struct OwnsData {
    current: String,
    stop: String,
}

impl OwnsData {
    // We'll have the traditional method that takes a reference
    // to self and returns an iterator over references into self.

    // Explicit lifetimes aren't needed, but it might help with understanding.
    // fn iter<'a>(&'a self) -> ThingIterator<'a> {

    fn iter(&self) -> ThingIterator {
        ThingIterator {
            current: &self.current,
            stop: &self.stop,
        }
    }
}

// Then, we'll implement IntoIterator for references to OwnsData
// using the OwnsData::iter method defined above.
// This is helpful because for loops and many iterator methods
// use IntoIterator to work.
impl<'a> IntoIterator for &'a OwnsData {
    // We'll be converting into ThingIterator
    type IntoIter = ThingIterator<'a>;
    type Item = &'a str;

    fn into_iter(self) -> ThingIterator<'a> {
        self.iter()
    }
}

fn main() {
    let pi = ThingIterator {
        current: "Ask",
        stop: "Zoo",
    };
    println!("Number of things={}", pi.count());

    // Alternatively, we could start with Strings
    // and use OwnsData
    let tau = OwnsData {
        current: "Ask".to_string(),
        stop: "Zoo".to_string(),
    };
    println!("Number of things={}", tau.iter().count());
}

(patio de recreo)

Véase también

PD la palabra que estás buscando es "elidida".

3
SCappella 29 dic. 2019 a las 12:34

No puedes, todavía. Esto requeriría tipos genéricos asociados (GAT) . A partir de hoy, sigue siendo solo un RFC.

La API actual Iterator / Stream a veces se llama "separada":

La idea es que Item que devuelve Stream se "separa" de self, lo que significa que se puede almacenar y mover independientemente de self.

Hipotéticamente, después de aterrizar GAT, uno podría escribir algo como:

trait AttachedStream {
    type Item<'s> where Self: 's;
    //       note the `'s` here!

    fn poll_next<'s>(
        self: Pin<&'s mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Option<Self::Item<'s>>>;
    //                         ^^^^
    // `'s` is the lifetime of the `self` reference.
    // Thus, the `Item` that gets returned may
    // borrow from `self`.
}

Eso es exactamente lo que quieres. Consulte Entrevista asíncrona de Niko # 2 para más detalles interesantes.

3
edwardw 29 dic. 2019 a las 12:30