如何将引用生存期绑定到函数本地作用域

时间:2019-10-01 12:07:14

标签: rust

我想编写一个函数A,该函数将一个函数B作为参数,该函数将一个由引用类型参数化的类型作为参数,该引用类型的生存期至少是A体内局部变量的生存期。

考虑以下示例:

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调用结束之前一直有效。

1 个答案:

答案 0 :(得分:3)

在实现过程中存在生命周期冲突,这是一个严重的问题,没有至少部分重新设计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的所有副本时,其中的项目也将删除。

这解决了两个问题:

  1. 您的other_foo现在已移入Arc中,并且不再引起生命周期问题
  2. 您现在可以像访问引用一样访问对象(Arc实现Deref

之所以选择Arc是因为您的FnOnce需要Send,而RcArc的单线程变体)无法提供。 / p>