Why array iter().next() returns reference?

let a = [1, 2, 3];

let mut iter = a.iter();

// A call to next() returns the next value...
assert_eq!(Some(&1), iter.next());

Here next() returns reference.
But the Item associated type of Iterator cannot use rreference.
So why the above codes could be compiled?

This is not true, the associated type Iterator::Item can absolutely be a reference. I think you are confusing this with the fact that it can't be a reference into data that is owned by the iterator, because the signature of Iterator::next() lacks the required lifetime annotations, i.e. it's not fn next(&'a mut self) -> Option<Self::Item> where Self::Item: 'a.

But .iter() methods, by convention, return an iterator that only borrows its data from the parent collection. Therefore this is perfectly fine. The fully expanded signature in this case is:

impl<'a, T> Iterator for Iter<'a, T> {
    fn next<'b>(&'b mut self) -> Option<&'a T> {
        ...
    }
}

which implies no such association between the lifetime of the iterator and the iterated items, so it's fine.

3 Likes

[T]::iter() has the signature:

fn(&'_ self)->std::slice::Iter<'_,T>

And Iter’s Iterator implementation says:

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;
    /* ... */
}

So, the Item type here is indeed a reference. As @H2CO3 points out, its lifetime comes from the original slice. That lifetime is collected from the iter method’s self argument and independent from the lifetime of Iter::next’s self reference.

1 Like

The Item asscociated type does not take a lifetime, so why next() returns reference?

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

Could you give a simple example? Like std::iter - Rust, how to return reference count value in next()?

I think you're running into misconception #1 in this list of lifetime misconceptions. A generic type T (or associated type like Iterator::Item) can be any type, including references and other types with lifetimes.

Here's an example on the playground where Iterator::Item has a lifetime.

4 Likes

You beat me to it. Here's my example:

struct MySliceIter<'a,T> {
    next_idx: usize,
    content: &'a [T],
}

impl<'a,T> Iterator for MySliceIter<'a,T> {
    type Item = &'a T;
    fn next(&mut self)->Option<&'a T> {
        self.content.get(self.next_idx)
            .map(|x| { self.next_idx += 1; x })
    }
}

fn main() {
    let v: Vec<usize> = vec![3,1,4,1,5,9,2,8];
    
    let my_iter = MySliceIter {
        next_idx: 0,
        content: &*v
    };

    // enumerate is here to demonstrate that the reference is
    // inside the iteration value, and not an artifact of for_each
    my_iter.enumerate()
           .for_each(|x:(usize, &usize)| { dbg!(x); });
}
1 Like

Thank you all!

Another side question is what's difference to gat?

https://github.com/rust-lang/rust/issues/44265

In our examples the associated type Iterator::Item is a fixed type within the context of the implementation -- for example the lifetimes of ColorIter<'a> and (&'a str, f32) must be the same. And thus the lifetime of the items returned from next() must be the same as well.

With GATs you could have an Iterator::Item with a generic lifetime, which could be determined at each call site to next(). This would allow "lending iterators", which is the motivating example for GATs. (Sometimes these are also called "streaming iterators", especially before async was implemented. But don't confuse them with async streams.)

You can read more about them in the RFC. Note in the example how Item and next have their own generic lifetime parameters.

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.