Lifetime error from Iterator with its Item as a reference

struct Data {
    v: Vec<i32>,
}

impl Iterator for Data {
    type Item = &[i32];
    fn next(&mut self)->Option<Self::Item> {
        let mut a = 0;
        let mut b = 0;
        // after some actions ...
        Some(&self.v[a..b])
    }
}

fn main() {}

The above code is to do some actions, and returns a slice from the next function. However, after doing some fixes based on the error hints, I still have not find a right method.
The compiling error is as follows:

error[E0637]: `&` without an explicit lifetime name cannot be used here
 --> src/main.rs:6:17
  |
6 |     type Item = &[i32];
  |                 ^ explicit lifetime name needed here
struct Data {
    v: Vec<i32>,
}

struct DataIter<'v> {
    v: &'v [i32],
    a: usize,
    b: usize,
}

impl<'v> Iterator for DataIter<'v> {
    type Item = &'v [i32];
    fn next(&mut self) -> Option<Self::Item> {
        let Self { v, mut a, mut b } = self;

        // after some actions ...
        Some(&v[a..b])
    }
}
7 Likes

To add on @vague's answer, you can then implement IntoIterator for &'a Data to construct a DataIter instance from it and make using it more ergonomic:

impl<'a> IntoIterator for &'a Data {
    type Item = &'a [i32];
    type IntoIter = DataIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        DataIter {
            v: self.v,
            a: 0,
            b: 0,
        }
    }
}
4 Likes

Collections are not supposed to be iterators themselves. The Iterator trait is forbidden from giving out references to any data it owns.

Results of the iterator must always remain valid even after the iterator is destroyed. This requirement makes .collect() possible.

6 Likes

The code replied can solve the problem, which give me a new light. And there are some other scenarios for example,

struct Data<I:Iterator> {
    i: I,
    v: Vec<<I as Iterator>::Item>,
}

impl<I:Iterator> Iterator for Data<I> {
    type Item = &[<I as Iterator>::Item]; // fixed from &<I as Iterator>::Item
    fn next(&mut self)->Option<Self::Item> {
        // after some action ...
        // for example, we can record the value selected
        let v = self.i.next()?;
        self.v.push(v);
        // ...
        let v = self.i.next()?;
        self.v.push(v);

        Some(self.v.as_slice())
    }
}

Errors below:

12 |     type Item = &[<I as Iterator>::Item];
   |                 ^ explicit lifetime name needed here

However, if I use the DataIter instead, another issue arises,

struct DataIter<'a,I:Iterator> {
    i: I,
    v: &'a [<I as Iterator>::Item],
}

impl<'a,I:Iterator> Iterator for DataIter<'a,I> {
    type Item = &'a [<I as Iterator>::Item]; // fixed from &'a <I as Iterator>::Item
    fn next(&mut self) -> Option<Self::Item> {
        // some action, for example
        // here, how do we record the item into self.v?
        let v = self.i.next()?;
        self.v.push(v);

        Some(self.v)
    }
}

impl<'a,I:Iterator> IntoIterator for &'a Data<I> {
    type Item = &'a [<I as Iterator>::Item];
    type IntoIter = DataIter<'a,I>;
    fn into_iter(self) -> Self::IntoIter {
        DataIter {
            i: self.i,
            v: self.v.as_slice(),
        }
    }
}

Errors below:

   |
31 |         self.v.push(v);
   |                ^^^^ method not found in `&[<I as Iterator>::Item]

As shown, encountering different issues in two scenarios.

Your latest examples are somewhat confused. However, one underlying theme is this:

So there is no[1] world where the Iterator implementor collects the results of some other iterator into itself, and then returns a reference (&_) to them. The specific errors you get in attempting to do this don't really matter; it's just not possible, so you need a different design.

You could return the same item as the nested iterator (I::Item, not &'_ I::Item).

You could return something with shared ownership (e.g. Arc<I::Item>) and store it in the implementing type as well, but it would be pretty non-idiomatic (unusual, weird).


  1. sound ↩︎

5 Likes

OMG, Sorry, I made a mistake and have the code at two places fixed in the implementing body.

1st, in Data<>'s body, the type Item fixed into a slice.

2nd, in DataIter<>'s body, the type item fixed into a slice too.

The main function of the code is to store the item value from the inner iterator that meets a certain condition and then return slice of these stored item with each calling of function next()

That is the design which is not possible.

The Iterator trait guarantees that this code is valid:

let a = iter.next()?;
let b = iter.next()?;
drop(iter);
use_both(a, b);

If dropping your iterator would invalidate any of the previously returned values, you can't use the Iterator trait, and even a million lifetime bounds won't solve that problem.

If you return a reference to self.v, then dropping the iterator would invalidate returned self.v references (Rust doesn't have a GC, so it can't make any reference live longer). You also can't modify self.v any more after it's been given out as shared/immutable.

4 Likes

Thanks, guys. All your replies has deeply helped me understand the lifetime design of Iterator. I rethink the problem, and maybe I should find another way to solve it.

You could do something with internal iteration (taking a closure to call instead of implementing Iterator). One option would be something like this, which lets the user do whatever calculation they need on the slice as long as their return value doesn't borrow from it:

struct Data<I:Iterator> {
    i: I,
    v: Vec<<I as Iterator>::Item>,
}

impl<I:Iterator> Data<I> {
    pub fn map_slice<O>(mut self, mut f:impl FnMut(&[I::Item])->O)->impl Iterator<Item=O> {
        std::iter::from_fn(move || {
            // after some action ...
            // for example, we can record the value selected
            let v = self.i.next()?;
            self.v.push(v);
            // ...
            let v = self.i.next()?;
            self.v.push(v);

            Some(f(self.v.as_slice()))
        })
    }
}
2 Likes

Thanks. It's really another way that runs well, and I will take it into consideration.

Filed Detect when user tries to write `Iterator` that incorrectly borrows from `Self` and would need GATs · Issue #125337 · rust-lang/rust · GitHub to improve the output in this case.

4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.