Assume the following contrived example:
struct Board {
squares: Vec<i32>,
}
struct Point<'a> {
board: &'a Board,
x: i32,
y: i32,
}
impl<'a> Point<'a> {
pub fn neighbors(&self) -> impl Iterator<Item = Point<'a>> {
[(0, -1), (-1, 0), (1, 0), (1, 0)]
.iter().map(|(dx, dy)| Point {
board: self.board,
x: self.x + dx,
y: self.y + dy,
})
}
}
This doesn't compile because from what I understand the lifetime of the points created in the lambda isn't correct:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:14:25
|
14 | .iter().map(|(dx, dy)| Point {
| _________________________^
15 | | board: self.board,
16 | | x: self.x + dx,
17 | | y: self.y + dy,
18 | | })
| |_____________^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 12:5...
--> src/main.rs:12:5
|
12 | / pub fn neighbors(&self) -> impl Iterator<Item = Point<'a>> {
13 | | [(0, -1), (-1, 0), (1, 0), (1, 0)]
14 | | .iter().map(|(dx, dy)| Point {
15 | | board: self.board,
... |
18 | | })
19 | | }
| |_____^
= note: ...so that the types are compatible:
expected &&Point<'_>
found &&Point<'a>
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 11:1...
--> src/main.rs:11:1
|
11 | impl<'a> Point<'a> {
| ^^^^^^^^^^^^^^^^^^
note: ...so that return value is valid for the call
--> src/main.rs:12:32
|
12 | pub fn neighbors(&self) -> impl Iterator<Item = Point<'a>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I'm a bit lost as to why this is the case though, because it seems like the lifetimes here make sense. A Point
's lifetime is caused by the lifetime of the reference to the Board
. Thus, a Point<'a>
has a reference to a board with lifetime 'a
so it should be able to create more Point<'a>
s because their board references will have the same lifetime ('a
).
But, if I remove the lambda, it works:
impl<'a> Point<'a> {
pub fn neighbors(&self) -> [Point<'a>; 4] {
[
Point { board: self.board, x: self.x , y: self.y - 1},
Point { board: self.board, x: self.x - 1, y: self.y },
Point { board: self.board, x: self.x + 1, y: self.y },
Point { board: self.board, x: self.x , y: self.y + 1},
]
}
}
So, I suspect the problem lies in the fact that the lambda may be run after the lifetime 'a
ends. But, does this mean that I can't lazily produce these points?
tl;dr How do I make the borrow checker happy with a method that lazily creates new structs whose lifetimes are tied to the struct creating them?
When you have this kind of issue in a method, a good thing to do is to add an explicit lifetime to &self
:
pub fn neighbors(&'a self) -> impl Iterator<Item = Point<'a>> {
[(0, -1), (-1, 0), (1, 0), (1, 0)]
.iter().map(|(dx, dy)| Point {
board: self.board,
x: self.x + dx,
y: self.y + dy,
})
}
The error is now better
error[E0373]: closure may outlive the current function, but it borrows `self`, which is owned by the current function
--> src/main.rs:14:30
|
14 | .iter().map(|(dx, dy)| Point {
| ^^^^^^^^^^ may outlive borrowed value `self`
15 | board: self.board,
| ---- `self` is borrowed here
help: to force the closure to take ownership of `self` (and any other referenced variables), use the `move` keyword
|
14 | .iter().map(move |(dx, dy)| Point {
| ^^^^^^^^^^^^^^^
You then just need to add the move
keyword as advised by the compiler, to say to it that you will not use &'a self
again.
Note that the lifetime of self
has not to be the same as the lifetime of Point
. This is better to use this signature:
fn neighbors<'b>(&'b self) -> impl 'b + Iterator<Item = Point<'a>>
Both the existing answers (Shepmaster, Boiethios) allow the Point
s returned by the iterator to outlive the iterator itself. However, it should be possible to build an iterator that even outlives the original Point
it was created from, by moving the contents of the Point
into it. This version retains the original function signature:
fn neighbors(&self) -> impl Iterator<Item = Point<'a>> {
let Point { board, x, y } = *self;
[(0, -1), (-1, 0), (1, 0), (1, 0)]
.iter().map(move |(dx, dy)| Point {
board: board,
x: x + dx,
y: y + dy,
})
}
Copying the contents of *self
into local variables which are moved into the closure makes it so the closure -- and therefore the returned iterator -- no longer contains any references to self
.
Here's something you can do with this version that can't be done otherwise (playground):
let mut p = Point {
board: &b,
x: 10,
y: 12,
};
for n in p.neighbors() {
p = n;
}
One potential caveat is that if Point
contains very large or non-Copy
data that can't or shouldn't be moved into the closure, this won't work. In that case you should use the other solution:
fn neighbors<'b>(&'b self) -> impl 'b + Iterator<Item = Point<'a>>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With