1

I'm following the tutorial Learn Rust With Entirely Too Many Linked Lists to learn Rust. The aim is to learn the various aspects of Rust by implementing various levels of linked lists. I'm stuck at implementing the (mutable) Iterator trait (here). These are my type definitions:

pub struct List<T> {
    head: Link<T>,
}
struct Node<T> {
    elem: T,
    next: Link<T>,
}
type Link<T> = Option<Box<Node<T>>>;

pub struct IntoIter<T>(List<T>);
pub struct Iter<'link, T>(&'link Link<T>);
pub struct IterMut<'link, T>(&'link mut Link<T>);

The tutorial has the following definitions for Iter and IterMut (everything else is the same as above):

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

So the difference in the iterator structs is that rather than storing optional (mutable) reference to nodes, as in the tutorial, I'm storing references to links instead.

Here is the implementation of the Iterator trait as in the tutorial:

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

    fn next(&mut self) -> Option<Self::Item> {
        self.next.map(|node| {
            self.next = node.next.map(|node| &node);
            &node.elem
        })
    }
}

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

    fn next(&mut self) -> Option<Self::Item> {
        self.next.take().map(|node| {
            self.next = node.next.as_deref_mut();
            &mut node.elem
        })
    }
}

And here is my implementation of the Iterator trait as per my type definitions:

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

    fn next(&mut self) -> Option<&'link T> {
        self.0.as_ref().map(|boxed_node: &'link Box<Node<T>>| {
            self.0 = &boxed_node.next;
            &boxed_node.elem
        })
    }
}

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

    fn next(&mut self) -> Option<Self::Item> {
        self.0.as_mut().map(|boxed_node| {
            self.0 = &mut boxed_node.next;
            &mut boxed_node.elem
        })
    }
}

Here is the error on compiling my implementation:

error: lifetime may not live long enough
  --> src/second.rs:96:9
   |
92 | impl<'link, T> Iterator for ListIterMut<'link, T> {
   |      ----- lifetime `'link` defined here
...
95 |     fn next(&mut self) -> Option<Self::Item> {
   |             - let's call the lifetime of this reference `'1`
96 |         self.0.as_mut().map(|boxed_node| {
   |         ^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'link`
   |
help: consider adding 'move' keyword before the nested closure
   |
96 |         self.0.as_mut().map(move |boxed_node| {
   |                             ++++

error[E0500]: closure requires unique access to `self.0` but it is already borrowed
  --> src/second.rs:96:29
   |
92 | impl<'link, T> Iterator for ListIterMut<'link, T> {
   |      ----- lifetime `'link` defined here
...
96 |         self.0.as_mut().map(|boxed_node| {
   |         ---------------     ^^^^^^^^^^^^ closure construction occurs here
   |         |
   |         borrow occurs here
   |         argument requires that `*self.0` is borrowed for `'link`
97 |             self.0 = &mut boxed_node.next;
   |             ------ second borrow occurs due to use of `self.0` in closure

If I compare my implementation and the tutorial's, then I guess what I'm missing is a version of Option::take for mutable references. I'm not even sure such a thing exists. Here are my queries:

  1. Does a version of Option::take for mutable references exist?
  2. I'm not able to understand the lifetime error.
  3. How do I fix my code without having to change my type definitions (while staying in safe-land)? Or is there no way to do that with the current type definitions?

To understand the lifetime error, I tried desugaring the code, adding explicit lifetimes everywhere, but I'm still not able to understand what's going wrong. Honestly, I'm still struggling with lifetimes a bit. I think I understand it and then something comes and knocks me off.

3
  • 2
    References must always be valid. Mutable references must always be exclusive. You can't store a mutable reference to a link in ListIterMut and also return a reference into that node from next. That's what take solves in the example implementation. Commented May 23, 2024 at 15:03
  • I see what you mean. That means no safe fix is possible with the current type definitions, right? Commented May 23, 2024 at 15:47
  • Not only is a safe fix impossible, I don't think there a sound (free of Undefined Behavior) solution with unsafe either. Commented May 24, 2024 at 14:17

1 Answer 1

0

This is indeed the problem.

Does a version of Option::take for mutable references exist?

No and yes. No, because something has to stay in place, or otherwise a panic could expose uninitialized memory. And yes, because you can put something else in place (quite hard in this case) or just handle panics differently.

I'm not able to understand the lifetime error.

The essence of it is that &'short mut &'long mut T (in this case, &mut self where Self = IterMut<'link, T> can only give you &'short mut T, not &'long mut T. Which means that the references you get from next and elem do not have the expected lifetime.

How do I fix my code without having to change my type definitions (while staying in safe-land)?

I'll leave that to you, with the information provided above.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.