[英]Lifetimes for method returning iterator of structs with same lifetime
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: 这不能编译,因为根据我的理解,在lambda中创建的点的生命周期是不正确的:
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
. Point
的生命周期是由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
). 因此,
Point<'a>
引用具有生命周期'a
的板,因此它应该能够创建更多的Point<'a>
因为它们的板引用将具有相同的生命周期( 'a
)。
But, if I remove the lambda, it works: 但是,如果我删除lambda,它的工作原理是:
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. 所以,我怀疑问题出在事实拉姆达一生可以后运行
'a
结束。 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? tl; dr如何让借用检查器满足于一种懒惰地创建新结构的方法,这些结构的生命周期与创建它们的结构相关联?
When you have this kind of issue in a method, a good thing to do is to add an explicit lifetime to &self
: 当你在一个方法中遇到这种问题时,一件好事就是为
&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. 然后你只需要按照编译器的建议添加
move
关键字,就可以说你不会再使用&'a self
。
Note that the lifetime of self
has not to be the same as the lifetime of Point
. 请注意,
self
的生命周期不能与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. 现有的答案( Shepmaster , Boiethios )都允许迭代器返回的
Point
比迭代器本身更长。 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. 但是,应该可以通过将
Point
的内容移动到其中来构建迭代器,该迭代器甚至比创建它的原始Point
更长。 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
. 将
*self
的内容复制到移动到闭包中的局部变量使得闭包 - 因此返回的迭代器 - 不再包含对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. 一个潜在的警告是,如果
Point
包含无法或不应该移入闭包的非常大或非Copy
数据,这将无效。 In that case you should use the other solution: 在这种情况下,您应该使用其他解决方案:
fn neighbors<'b>(&'b self) -> impl 'b + Iterator<Item = Point<'a>>
I would assign a lifetime to self
that is distinct from the lifetime of the Board
: 我会为
self
分配一个与Board
生命周期不同的生命:
impl<'b> Point<'b> {
fn neighbors<'a>(&'a self) -> impl Iterator<Item = Point<'b>> + 'a {
[(0, -1), (-1, 0), (1, 0), (1, 0)]
.iter().map(move |(dx, dy)| Point {
board: self.board,
x: self.x + dx,
y: self.y + dy,
})
}
}
This also requires marking the closure as move
to move the &Self
value into the closure so that the closure can still access board
, x
and y
when it is advanced. 这也需要将闭合标记为
move
以将&Self
值移动到闭合中,以便闭合仍然可以在它前进时接近board
x
和y
。 Since immutable references can be copied, this won't prevent the caller from doing anything. 由于可以复制不可变引用,因此不会阻止调用者执行任何操作。
Without the separate lifetimes, the lifetimes of the Point
s returned from the iterator are artificially limited to the lifetime of the Point
that generated them. 没有单独的生命周期,从迭代器返回的
Point
的生命周期被人为地限制为生成它们的Point
的生命周期。 As an example, this code fails when the lifetimes are unified, but works when they are distinct: 例如,此代码在生命周期统一时失败,但在它们不同时起作用:
fn example<'b>(board: &'b Board) {
let _a = {
let inner = Point { board, x: 0, y: 0 };
let mut n = inner.neighbors();
n.next().unwrap()
};
}
See also: 也可以看看:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.