4
votes

I'm getting to grips with Rust by trying to implement a singly-linked list. Here are my data structures:

struct Linked<T> {
    head: Option<Box<Node<T>>>
}

struct Node<T> {
    data: T,
    next: Option<Box<Node<T>>>
}

Now, I'd like to add an iterator to this:

struct LinkedIter<'a, T: 'a> {
    node: Option<&'a Node<T>>,
}

I've written a .iter() method for Linked<T>, which compiles and works fine.

impl<T> Linked<T> {
    fn iter(&self) -> LinkedIter<T> {
        LinkedIter { node: match self.head {
                Some(ref node) => Some(&**node),
                None           => None
            }
        }
    }
}

Now, this match block is converting an Option<Box<Linked<T>>> to an Option<&Linked<T>>. This is exactly what the Option::map() method is for. So I reimplemented this method, using head.as_ref() instead of head to avoid taking ownership of the Option's contents in the closure.

impl<T> Linked<T> {
    fn iter(&self) -> LinkedIter<T> {
        LinkedIter { node:
            self.head.as_ref().map(|b: &Box<Node<T>>| &**b)
        }
    }
}

Unfortunately, the reference created in the closure cannot be allowed to outlive the closure, because it refers to something passed into the closure as a parameter. The compiler complains (paraphrased a bit):

error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements:

first, the lifetime cannot outlive the anonymous lifetime #1 defined [in the closure]

but, the lifetime must be valid for [the lifetime of the iter function]

so that the [node:] expression is assignable

How can I explain to the compiler that the reference will still be valid after the closure ends?

(Playground)

1
I'm setting up a minimal example on the Rust Playground. Your code does fail to compile, but not the way you describe it to (at least, that's not the first error coming up). Anyway, here's the link, it should provide a good starting point to solve your specific problem: is.gd/HleNDJmdup
And here is the minimal example which does trigger your error: is.gd/laiDuZmdup
Whoops, I took the lifetime specifiers out of the code when I moved it to SO, forgetting that they were necessary. Lemme add those back, thanks. The second one does indeed fail the way I'm asking about. Thanks for the MWE link.George Hilliard

1 Answers

4
votes

The idea is to inform the compiler that the reference you're taking in map() lives as long as the iterator. I got it working like that:

impl<T> Linked<T> {
    fn iter<'a>(&'a self) -> LinkedIter<T> {
        LinkedIter { node:
            self.head.as_ref().map(|b: &'a Box<Node<T>>| &**b)
        }
    }
}