【问题标题】:Lifetime annotation for closure argument闭包参数的生命周期注释
【发布时间】:2017-06-10 12:44:23
【问题描述】:

我想编译以下代码:

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 link

这是我得到的编译错误:

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 说“那个人的一生,在那里”。
  • 不知道如何让它工作。作为一种解决方法,也许您也可以让闭包从环境中捕获 s,而不是将其作为参数传递。喜欢this
  • @Shepmaster 我也发现了这一点。这也是解决实际问题的解决方案 :) 尽管如此,我还是很惊讶无法在此处手动指定 s 的类型。

标签: rust lifetime


【解决方案1】:

如果删除所有生命周期注释并让编译器推理完成其工作,您的代码就可以正常工作:

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);
};

这在闭包上声明了一个新的生命周期。使用编造的语法(你can't declare lifetimes on closure arguments),它相当于:

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

这就是编译器出现错误的原因:

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

生成的生命周期与Provider 的生命周期之间没有联系。省略它允许编译器插入所需但无法命名的生命周期。

【讨论】:

  • 是否有任何工作/建议可以在未来的 rust 版本中明确指定 s 的类型?我想这需要某种生命周期规范,说明引用与 具有相同的生命周期,所以我可以写 |s: &amp;'liftime_of(provider) str
  • @bennofs 我不知道有这样的方法或建议允许明确引用局部变量的生命周期。
  • 今天我了解到推理可以比手动指定生命周期更强大(而不是更方便)......谢谢!
  • @PaoloFalabella 一开始我很困惑,因为我总是努力删除任何显式的生命周期/类型。在某些时候,我遇到了奇怪的问题,因为提问者有明确的生命周期,而我没有,所以我无法弄清楚是什么被破坏了。现在我想起来了。
  • 你能解释一下为什么原来的注释会停止工作吗?在我看来,它们都与我期望编译器去省略或推断的不同。
【解决方案2】:

这是一个可以编译的版本:

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 link

我做了以下更改:

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

最后一个有点令人困惑,因为显然只是在生命周期中添加了一个名称(显然没有添加任何约束)就可以了。

认为(但我喜欢一些文档)这是因为终身省略。闭包实际上是一个隐藏的struct,带有fn call(&amp;self, s: &amp;str)(在这种情况下)方法。根据lifetime elision ruless 参数获得与&amp;self 相同的生命周期,即闭包本身。在这种情况下,闭包是在vec之后声明的,所以生命周期太短了。显式生命周期意味着它与闭包自身的生命周期解耦。

【讨论】:

  • 这个答案当然很有趣,并为我提供了一些关于生命周期的新信息,但不幸的是,在我的问题中,Provider 是一个Arena,它没有任何它拥有的数据:( 所以引用确实不会比提供者本身寿命长。
  • 是什么决定了get_string 的结果在您的案例中存在多长时间?
  • 它的寿命和provider 一样长
  • 在哪种情况下PhantomData 不会像答案中那样做你想要的?
  • 考虑StringProvider 拥有的情况(例如通过属性)。示例(编辑错误链接):play.rust-lang.org/…
猜你喜欢
  • 2015-10-02
  • 2021-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多