2

In Learning Rust With Entirely Too Many Linked Lists, the iter() creates an iterator:

impl<T> List<T> {
    pub fn iter(&self) -> Iter<T> {
        Iter { next: self.head.as_ref().map(|node| &**node) }
    }
}

I try to test if the iterator prevents further modification on the list:

let mut list = List::new();
list.push(1);

let mut iter = list.iter();
list.pop();

The compiler reports an error:

list2.rs:114:1: 114:5 error: cannot borrow `list` as mutable because it is also borrowed as immutable [E0502]
list2.rs:114 list.pop();
             ^~~~
list2.rs:113:24: 113:28 note: previous borrow of `list` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `list` until the borrow ends
list2.rs:113         let mut iter = list.iter();
                                    ^~~~

It seems that Rust would indeed prevent unsafe operations, but from the syntax, why does list.iter() borrow the list? In the method, it just return the reference of the head element.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
kingluo
  • 1,679
  • 1
  • 13
  • 31

1 Answers1

5

From the linked article:

pub struct Iter<T> {
    next: Option<&Node<T>>,
}

So the iterator has a reference. Expanding to make the lifetime more explicit:

pub struct Iter<'a, T> {
    next: Option<&'a Node<T>>,
}

Now looking at the iter method:

impl<T> List<T> {
    pub fn iter(&self) -> Iter<T> {
        ...
    }
}

I'm leaving out the implementation for clarity, since Rust's borrowing rules stop at the declaration too. Now if we put the elided types back in:

pub fn iter<'a>(&'a self) -> Iter<'a, T> {...}

Since there is only a &self argument, the return value gets the same elided lifetime. See the elided lifetimes documentation.

The end result is that the Iter<'a, T> has the same lifetime as the reference passed to the iter() method - so that reference's lifetime must be extended to at least as long as the Iter object's. Therefore you can't borrow mutably (for the pop method) while the Iter is alive.

There is a good reason for this; if the pop were to succeed, the iterator would then have a dangling reference to the former head, leading to memory unsafety.

Chris Emerson
  • 13,041
  • 3
  • 44
  • 66