¿Puedo escribir un iterador que se muta y luego produce una referencia en sí mismo?

Me encontré con un problema que se simplifica en lo siguiente:

struct MyIter { vec: Vec, } fn fill_with_useful_data(v: &mut Vec) { /* ... */ } impl Iterator for MyIter { type Item = &'a [i8]; fn next(&mut self) -> Option { fill_with_useful_data(&mut self.vec); Some(&self.vec) } } fn main() { for slice in (MyIter { vec: Vec::new() }) { println!("{}", slice); } } 

Esto genera el error:

 error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates --> src/main.rs:9:6 | 9 | impl Iterator for MyIter { | ^^ unconstrained lifetime parameter 

La idea es que el iterador hace un montón de trabajo que se refleja en sus campos y en cada paso, produce una referencia en sí mismo al código de llamada. En este caso, podría modelarlo produciendo una copia del estado en lugar de la referencia, pero pretendamos que no es posible o simplemente es inconvenientemente costoso.

Intuitivamente esto no debería ser un problema porque el comprobador de préstamos puede garantizar que .next() no se vuelva a llamar mientras que la referencia producida se puede seguir utilizando para inspeccionar el estado del iterador, pero el rasgo del Iterator no parece proporcionar eso tipo de cosas directamente. Incluso con algunas permutaciones como solo mantener una referencia al vector en el iterador mismo o hacer que el iterador sea una referencia o algo para obtener las vidas horneadas en el tipo anterior, no puedo obtener nada más allá del comprobador de préstamos.

Leí la entrada de blog ” Iterators yielding mutable references “, pero no estoy seguro si / cómo se aplica a mi problema que no involucra referencias mutables.

Esto no es posible. Si se permitiera, se podría volver a llamar a next y, por lo tanto, modificar los datos que también son visibles a través de & incluso invalidar la referencia por completo. Esto se debe a que no existe conexión entre el objeto en sí mismo y la referencia devuelta: no existe una vida explícita que los vincule.

Para que el comstackdor razone sobre esto y permita devolver una referencia en self necesita una firma como

 fn next(&'a mut self) -> Option< &'a [i8]> 

Sin embargo, esto difiere de la firma del rasgo que no está permitido como código genérico que solo toma una T: Iterator< ...> no puede decir que existen diferentes requisitos sobre el uso del valor de retorno para algo de T ; todos tienen que ser manejados de manera idéntica.

El rasgo de Iterator está diseñado para valores de retorno que son independientes del objeto iterador, que es necesario para que los adaptadores de iterador como .collect sean correctos y seguros. Esto es más restrictivo de lo necesario para muchos usos (por ejemplo, un uso transitorio dentro de un ciclo for ) pero es así en este momento. No creo que tengamos las herramientas para generalizar este rasgo de rasgos / para ahora (específicamente, creo que necesitamos tipos asociados con tiempos de vida más altos), pero tal vez en el futuro.