方法返回具有相同生命周期的结构迭代器的生命周期
Lifetimes for method returning iterator of structs with same lifetime
假设以下人为示例:
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,
})
}
}
这无法编译,因为根据我的理解,在 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>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
虽然我有点迷惑为什么会这样,因为这里的生命周期似乎是有道理的。 Point
的生命周期是由对 Board
的引用的生命周期引起的。因此,Point<'a>
引用了一个生命周期为 'a
的板,因此它应该能够创建更多的 Point<'a>
,因为它们的板引用将具有相同的生命周期('a
).
但是,如果我删除 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},
]
}
}
所以,我怀疑问题在于 lambda 可能在生命周期 'a
结束后 运行。但是,这是否意味着我不能懒惰地产生这些点数?
tl;dr 如何让借用检查器对延迟创建新结构的方法感到满意,这些新结构的生命周期与创建它们的结构相关联?
当您在方法中遇到此类问题时,最好的办法是向 &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,
})
}
错误现在更好
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 {
| ^^^^^^^^^^^^^^^
然后您只需按照编译器的建议添加 move
关键字,告诉它您将不再使用 &'a self
。
请注意 self
的生命周期不必与 Point
的生命周期相同。这个最好用这个签名:
fn neighbors<'b>(&'b self) -> impl 'b + Iterator<Item = Point<'a>>
我会为 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,
})
}
}
这还需要将闭包标记为 move
以将 &Self
值移动到闭包中,以便闭包仍然可以访问 board
、x
和 y
先进时。由于可以复制不可变引用,因此这不会阻止调用者执行任何操作。
没有单独的生命周期,从迭代器返回的 Point
的生命周期被人为限制为生成它们的 Point
的生命周期。例如,此代码在生命周期统一时失败,但在生命周期不同时有效:
fn example<'b>(board: &'b Board) {
let _a = {
let inner = Point { board, x: 0, y: 0 };
let mut n = inner.neighbors();
n.next().unwrap()
};
}
另请参阅:
两个现有答案 (, ) 都允许迭代器返回的 Point
比迭代器本身更长寿。但是,通过将 Point
的内容移入其中,构建一个甚至比创建它的原始 Point
更长寿的迭代器应该是可能的。此版本保留原函数签名:
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,
})
}
将 *self
的内容复制到移动到闭包中的局部变量中,使得闭包——因此返回的迭代器——不再包含对 self
的任何引用。
这里有一些你可以用这个版本做的事情,否则不能做 (playground):
let mut p = Point {
board: &b,
x: 10,
y: 12,
};
for n in p.neighbors() {
p = n;
}
一个潜在的警告是,如果 Point
包含不能或不应移入闭包的非常大或非 Copy
数据,这将不起作用。在这种情况下,您应该使用其他解决方案:
fn neighbors<'b>(&'b self) -> impl 'b + Iterator<Item = Point<'a>>
假设以下人为示例:
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,
})
}
}
这无法编译,因为根据我的理解,在 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>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
虽然我有点迷惑为什么会这样,因为这里的生命周期似乎是有道理的。 Point
的生命周期是由对 Board
的引用的生命周期引起的。因此,Point<'a>
引用了一个生命周期为 'a
的板,因此它应该能够创建更多的 Point<'a>
,因为它们的板引用将具有相同的生命周期('a
).
但是,如果我删除 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},
]
}
}
所以,我怀疑问题在于 lambda 可能在生命周期 'a
结束后 运行。但是,这是否意味着我不能懒惰地产生这些点数?
tl;dr 如何让借用检查器对延迟创建新结构的方法感到满意,这些新结构的生命周期与创建它们的结构相关联?
当您在方法中遇到此类问题时,最好的办法是向 &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,
})
}
错误现在更好
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 {
| ^^^^^^^^^^^^^^^
然后您只需按照编译器的建议添加 move
关键字,告诉它您将不再使用 &'a self
。
请注意 self
的生命周期不必与 Point
的生命周期相同。这个最好用这个签名:
fn neighbors<'b>(&'b self) -> impl 'b + Iterator<Item = Point<'a>>
我会为 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,
})
}
}
这还需要将闭包标记为 move
以将 &Self
值移动到闭包中,以便闭包仍然可以访问 board
、x
和 y
先进时。由于可以复制不可变引用,因此这不会阻止调用者执行任何操作。
没有单独的生命周期,从迭代器返回的 Point
的生命周期被人为限制为生成它们的 Point
的生命周期。例如,此代码在生命周期统一时失败,但在生命周期不同时有效:
fn example<'b>(board: &'b Board) {
let _a = {
let inner = Point { board, x: 0, y: 0 };
let mut n = inner.neighbors();
n.next().unwrap()
};
}
另请参阅:
两个现有答案 (Point
比迭代器本身更长寿。但是,通过将 Point
的内容移入其中,构建一个甚至比创建它的原始 Point
更长寿的迭代器应该是可能的。此版本保留原函数签名:
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,
})
}
将 *self
的内容复制到移动到闭包中的局部变量中,使得闭包——因此返回的迭代器——不再包含对 self
的任何引用。
这里有一些你可以用这个版本做的事情,否则不能做 (playground):
let mut p = Point {
board: &b,
x: 10,
y: 12,
};
for n in p.neighbors() {
p = n;
}
一个潜在的警告是,如果 Point
包含不能或不应移入闭包的非常大或非 Copy
数据,这将不起作用。在这种情况下,您应该使用其他解决方案:
fn neighbors<'b>(&'b self) -> impl 'b + Iterator<Item = Point<'a>>