请考虑以下示例:
struct Foo {}
fn consume(mut v: Vec<&Foo>) {
while let Some(..) = v.pop() {
// Do stuff
continue;
}
}
fn setup_and<F>(f: F)
where
F: FnOnce(&mut Vec<&Foo>) + Send,
{
let mut v: Vec<&Foo> = vec![];
let other_foo = Foo {};
f(&mut v);
v.push(&other_foo);
consume(v);
}
fn main() {
let foo = Foo {};
setup_and(|v| {
v.push(&foo);
});
}
rustc无法自行推断寿命。它抱怨:
error[E0597]: `foo` does not live long enough
--> src/main.rs:25:17
|
24 | setup_and(|v| {
| --- value captured here
25 | v.push(&foo);
| --------^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `foo` is borrowed for `'static`
26 | });
27 | }
| - `foo` dropped here while still borrowed
我试图为setup_and
所采用的引用指定生存期,如下所示:
fn setup_and<'a, F>(f: F)
where
F: FnOnce(&mut Vec<&'a Foo>) + Send,
{
let mut v: Vec<&'a Foo> = vec![];
[现在rustc抱怨setup_and
本地参考other_foo
寿命不足。我认为这是因为它要比setup_and
的范围更长的生存期。
在这种情况下,我将如何正确地绑定生命周期?我想表示这些引用在consume
调用结束之前必须一直有效。
您在实现中存在生命周期冲突,这是一个严重的问题,没有至少部分重新设计struct
和方法的外部签名,就没有简单的解决方法。它们都源自setup_and
方法,当您明确描述生命周期界限时,编译器会突出显示它们。
方法的主体复制到下面,并带有注释,供您了解问题:
let mut v: Vec<&Foo> = vec![]; let other_foo = Foo {}; // other_foo is created here f(&mut v); v.push(&other_foo); // other_foo requires lifetime 'a to be added to this consume(v); // consume does not restrict the lifetime requirement 'a // other_foo is dropped here, at lifetime less than 'a
此问题最简单的解决方案是存储
Arc<Foo>
,就像这样(playground):
fn setup_and<F>(f: F) where F: FnOnce(&mut Vec<Arc<Foo>>) + Send, { let mut v: Vec<Arc<Foo>> = vec![]; let other_foo = Foo {}; f(&mut v); v.push(Arc::new(other_foo)); consume(&mut v); }
Arc
是原子引用计数指针。它是一个可克隆的结构,用作指向堆上对象的动态指针。出于所有目的和目的,它可以作为只读参考,而无需使用期限。删除Arc
的所有副本时,其中的项目也将删除。
这解决了两个问题:
other_foo
现在已移动到了Arc
中,不再引起其生命周期问题Arc
实现Deref
)选择Arc
是因为您的FnOnce
需要Send
,而Rc
(Arc
的单线程变体)无法提供。