闭包参数的生命周期注解

9
我希望能够使以下代码编译通过:
struct Provider {}

impl Provider {
    fn get_string<'a>(&'a self) -> &'a str { "this is a string" }
}

fn main() {
    let provider = Provider{};
    let mut vec: Vec<&str> = Vec::new();

    // PROBLEM: how do I say that this reference s here
    // needs to live as long as vec?
    let fun = |s: &str| {
        vec.push(s);
    };

    fun(provider.get_string());
}

这是Playground链接

我收到的编译错误如下:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
 --> src/main.rs:9:22
  |
9 |     let mut vec: Vec<&str> = Vec::new();
  |                      ^^^^
  |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the block at 11:24...
 --> src/main.rs:11:25
  |
11|     let fun = |s: &str| {
  |                         ^
note: ...so that reference does not outlive borrowed content
 --> src/main.rs:12:18
  |
12|         vec.push(s);
  |                  ^
note: but, the lifetime must be valid for the block suffix following statement 2 at 13:6...
 --> src/main.rs:13:7
  |
13|     };
  |       ^
note: ...so that variable is valid at time of its declaration
 --> src/main.rs:11:9
  |
11|     let fun = |s: &str| {
  |         ^^^

嗯!这很有趣。有时我真希望我可以使用'provider来表示“那个家伙的寿命在那里”。 - Matthieu M.
不确定如何使其工作。作为一种解决方法,也许你可以让闭包从环境中捕获 s,而不是将其作为参数传递。像这样 - Paolo Falabella
@Shepmaster 我后来也发现了。那也是适用于真正问题的解决方案 :) 不过,我很惊讶在这里无法手动指定 s 的类型。 - bennofs
2个回答

8

如果删除所有的生命周期注解并让编译器推断工作,您的代码将正常运行:

struct Provider;

impl Provider {
    fn get_string(&self) -> &str { "this is a string" }
}

fn main() {
    let provider = Provider;
    let mut vec = Vec::new();

    let mut fun = |s| {
        vec.push(s);
    };

    fun(provider.get_string());
}

简而言之,无法显式地引用局部变量的生存周期,只能引用函数参数的生存周期。但编译器知道如何处理。
如果您确实需要,可以创建一个函数来允许对生命周期进行注释。
fn thing<'a>(provider: &'a Provider) -> Vec<&'a str> {
    let mut vec: Vec<&'a str> = Vec::new();

    {
        let mut fun = |s: &'a str| vec.push(s);

        fun(provider.get_string());
    } // End mutable borrow of `vec`

    vec
}

fn main() {
    let provider = Provider;
    thing(&provider);
}

为什么原始注释会导致事情停止工作?
具体而言,是这部分内容:
let fun = |s: &str| {
    vec.push(s);
};

这个声明在闭包中创建了一个新的生命周期。使用一个虚构的语法(你不能在闭包参数上声明生命周期),它相当于:

let fun = <'a> |s: &'a str| {
    vec.push(s);
};

因此编译器出现了以下错误:

生命周期不能超过[闭包块]上定义的匿名生命周期#1

这个生成的生命周期与Provider的生命周期之间没有任何关联。省略它可以让编译器插入所需但无法命名的生命周期。


有没有关于在未来的 Rust 版本中明确指定 s 类型的工作/提案?我猜需要的是一些生命周期规范,以表明该引用与<其他引用>具有相同的生命周期,这样我就可以写成 |s: &'lifetime_of(provider) str - bennofs
@bennofs 我不知道有任何明确引用局部变量生命周期的方法或建议。 - Shepmaster
今天我学到了推断可以比手动指定生命周期更强大(而不仅仅是更方便)...谢谢! - Paolo Falabella
1
@PaoloFalabella,一开始我觉得很困惑,因为我总是努力消除任何显式的生命周期/类型。但有一次,当一个提问者使用了显式的生命周期,而我没有时,我就遇到了一些奇怪的问题,所以我无法找出哪里出了问题。现在我记住了。 - Shepmaster
你能解释一下为什么原始的注释会导致程序无法正常工作吗?在我看来,它们都没有任何不同,与编译器推断或还原的结果相同。 - Chris Emerson

3

以下是可编译的版本:

use std::marker::PhantomData;

struct Provider<'a> {
    _dummy: PhantomData<&'a ()>,
}

impl<'a> Provider<'a> {
    fn get_string(&self) -> &'a str {
        "this is a string"
    }
}

fn f<'b>() {
    let provider = Provider { _dummy: PhantomData };
    let mut vec: Vec<&str> = Vec::new();

    // PROBLEM: how do I say that this reference s here
    // needs to live as long as vec?
    let mut fun = |s: &'b str| { vec.push(s); };

    fun(provider.get_string());
}

fn main() {
    f()
}

Playground链接

我进行了以下更改:

  • Provider中添加了一个生命周期(我添加了PhantomData,但我猜想您的提供程序已经拥有一些它将提供的数据)。
  • 更新get_string方法以显示它返回具有提供程序生命周期而不是输入生命周期的内容(即基于Provider的生命周期参数)。
  • 向函数添加了一个新的生命周期参数'b(我将其重命名为f(),因为main()不能有一个),我用它来命名闭包参数的生命周期。

最后一个略微令人困惑,因为显然仅仅给生命周期命名(似乎没有添加任何约束)就使其工作了。

我认为(但我很希望有一些文档来证实这一点),这是因为生命周期省略。闭包实际上是一个隐藏的struct,具有一个fn call(&self, s: &str)(在这种情况下)方法。根据生命周期省略规则s参数获得与&self相同的生命周期,即闭包本身的生命周期。在这种情况下,闭包在vec之后声明,因此生命周期太短。显式生命周期意味着它与闭包自身的生命周期分离。


这个答案确实很有趣,为我提供了一些关于生命周期的新信息,但不幸的是,在我的问题中,“Provider”是一个“Arena”,它没有任何自己拥有的数据:(因此,引用确实不会比提供程序本身存在更长时间。 - bennofs
在您的情况下,是什么决定了get_string结果的生命周期? - Chris Emerson
它的生命周期与“提供者”完全相同。 - bennofs
在什么情况下,PhantomData 无法像答案中所述的那样实现你想要的功能? - Chris Emerson
考虑 Provider 拥有的 String 的情况(例如通过属性)。示例(已编辑错误链接):https://play.rust-lang.org/?gist=4c97845847bbda978b41f30b26a0f7db&version=stable&backtrace=0 - bennofs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接