Implementation of Variable Iterators

I'm new to rust and I've been stuck with this problem for a while: Iter iterators that do the same thing compile, but IterMut does not.
I don't understand why the mutator interprets &mut self's lifecycle as '1 instead of 'a? The fact that the Iter iterator compiles means that the compiler interprets &mut selfs lifecycle as 'a, so why doesn't it work for IterMut
I found two related threads, but neither mentioned this. Why it is wrong for IterMut to use this pattern?
The mutable reference lifetime problem of iterators
How does Vec’s iterator return a mutable reference

pub struct IterMut<'a, T> {
    inner: &'a mut Vec<T>,
    index: usize,
}

impl<'a, T> Iterator for IterMut<'a, T> {
    type Item = &'a mut T;

    fn next(&mut self) -> Option<Self::Item> {
        let item = self.inner.get_mut(self.index);
        self.index += 1;
        item
    }
}

pub struct Iter<'a, T> {
    inner: &'a Vec<T>,
    index: usize,
}

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

    fn next(&mut self) -> Option<Self::Item> {
        let item = self.inner.get(self.index);
        self.index += 1;
        item
    }
}
error: lifetime may not live long enough
    --> src/main.rs:1984:9
     |
1978 | impl<'a, T> Iterator for IterMut<'a, T> {
     |      -- lifetime `'a` defined here
...
1981 |     fn next(&mut self) -> Option<Self::Item> {
     |             - let's call the lifetime of this reference `'1`
...
1984 |         item
     |         ^^^^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`

And the third omission rule for lifecycle doesn't seem to work here either. The first code compiles fine, but when I don't identify the lifecycle for the Item, the compiler won't allow it.

pub struct Iter<'a, T> {
    inner: &'a Vec<T>,
    index: usize,
}

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

    fn next(&mut self) -> Option<Self::Item> {
        let item = self.inner.get(self.index);
        self.index += 1;
        item
    }
}
pub struct Iter<'a, T> {
    inner: &'a Vec<T>,
    index: usize,
}

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

    fn next(&mut self) -> Option<Self::Item> {
        let item = self.inner.get(self.index);
        self.index += 1;
        item
    }
}
error[E0637]: `&` without an explicit lifetime name cannot be used here
    --> src/main.rs:1994:17
     |
1994 |     type Item = &T;
     |                 ^ explicit lifetime name needed here

error: lifetime may not live long enough
    --> src/main.rs:1999:9
     |
1993 | impl<'a, T> Iterator for Iter<'a, T> {
     |      -- lifetime `'a` defined here
...
1999 |         item
     |         ^^^^ returning this value requires that `'a` must outlive `'static`

Mutable iterators often require unsafe, because the borrow checker is unable to check whether you always give separate non-overlapping &mut references.

Iterator code using get_mut(i) will never compile as safe. You could use a pre-existing iterator, or split_first() (although that might also require some clever implementation to get lifetimes right).

Or use unsafe raw pointer cast and dereference to force the lifetime you need.

2 Likes

To elaborate why the obvious code as you are trying to do is not allowed, in case kornel's answer wasn't enough:

There's nothing to prevent other code from calling next() multiple times while still holding the &mut reference returned by the previous next() call. Even though in this case you increment the index, Rust can't prove that using its borrowing rules, so it doesn't allow it.

For instance, suppose user code did

let mut iter = ...;
let x = iter.next().unwrap();
let y = iter.next().unwrap();
// Now x and y are both mutable references to T. They are not allowed to both
// refer to the same value.
// But if you were to change your line `self.index += 1;` to `self.index += 0;`
// then you would have violated ownership/borrowing rules. So Rust can't
// allow it.

Also see the useful explanation in this SO answer by Peter Hall.

1 Like

To answer this part directly: shared references (&_) implement Copy, so here:

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<Self::Item> {
        let item = self.inner.get(self.index);

You effectively call get using a copy of self.inner, and can thus get a &'a Self::Item back out.

But exclusive references (&mut _) do not implement Copy, and you cannot soundly borrow a &'long mut _ through a &'short mut _ &'long mut _. If you could, it would be possible to do the other UB things which the others have mentioned, like getting multiple &mut to the same Vec contents.

So you can only reborrow a &'short mut Vec<_> in the call to get_mut, and can't get a &'long mut Self::Item ('a) back out.

See also this example which walks through making an iterator over &mut [_], which is possible without unsafe and could be a drop-in replacement for your &mut Vec<_> based attempt.

2 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.