【问题标题】:Function works with inferred lifetime but not explicit lifetime函数适用于推断的生命周期,但不适用于显式生命周期
【发布时间】:2021-06-13 18:36:24
【问题描述】:

在这段代码中:

struct Obj<'a> {
    inside: &'a mut i32
}

fn take_and_return<'o>(obj: Obj<'o>) -> Obj<'o> {
    obj
}

fn run_me_1() {
    let mut v = 42;
    let s: Obj<'_> = Obj {
        inside: &mut v
    };
    take_and_return(s);
}

我想在run_me_1 中为s 引入命名生命周期。

我使用了 Rust Analyzer 的建议:

fn run_me_2<'a>() {
    let mut v = 42;
    let s: Obj<'a> = Obj {
        inside: &mut v
    };
    take_and_return(s);
}

然后我得到以下错误:

error[E0597]: `v` does not live long enough
  --> src/lib.rs:20:11
   |
17 | fn run_me_2<'a>() {
   |             -- lifetime `'a` defined here
18 |     let mut v = 42;
19 |     let s: Obj<'a> = Obj {
   |            ------- type annotation requires that `v` is borrowed for `'a`
20 |         inside: &mut v
   |                 ^^^^^^ borrowed value does not live long enough
...
23 | }
   | - `v` dropped here while still borrowed

我的理解是take_and_return拥有obj的所有权,所以obj必须永远存在,所以'o必须永远存在。这就解释了为什么run_me_2 无法编译。

我的问题是:

  • 为什么run_me_1 编译?
  • 推断者在run_me_1 中的'_ 中添加了什么?
  • 如何修复run_me_2 以便编译?

Playground link to code

【问题讨论】:

  • 我认为这是一个 XY 问题。当您没有任何输入参数受其限制时,为什么需要该通用生命周期参数'a?如果您不使用它,那么只需将其删除 - 问题就解决了。

标签: rust lifetime


【解决方案1】:

我的理解是 take_and_return 拥有obj 的所有权,所以obj 必须永远存在,所以'o 必须永远存在。

如果该值被拥有并且具有'static 的生命周期界限,则这种陈述是正确的,通常表示为T: 'statictake_and_return 不需要 T: 'static,它采用与生命周期 'o 关联的具体类型,它不能超过该生命周期(因为它包含对该生命周期的引用)。

run_me_1 编译不是因为对象是静态的,而是因为变量v 明显超过了take_and_return 返回的值,该值立即被删除,而v 仍然存在。如果您修改了run_me_1,使得take_and_return() 返回的值实际上比v 的寿命长,它也将无法编译。例如:

fn run_me_1_modified() {
    let x;
    {
        let mut v = 42;
        let s: Obj<'_> = Obj { inside: &mut v };
        x = take_and_return(s);
    }
    println!("{:p}", x.inside);  // or use x in any other way
}

推断者在run_me_1 中的'_ 中添加了什么?

它设置了一个匿名生命周期,对应于v 所在的源部分。

run_me_2() 是另一种野兽。它基本上说,“我会让我的调用者选择它希望的任何生命周期,然后我将创建一个与 那个 生命周期相关联的Obj,之后我会将它传递给take_and_return(),获取具有相同生命周期的Obj,然后将其销毁。这是不对的,因为Obj { &amp;mut v } 明确要求Obj 指定的生命周期是v 生命周期的子集。但我们是从技术上讲,允许我们的调用者选择obj 的生命周期,所以它还不如使用run_me_2&lt;'static&gt; 调用我们。

请注意,像fn run_me_2&lt;'a&gt;() { ... } 这样的声明没有太大意义,因为只有静态生命周期可以在不引用先前定义的值的情况下命名。通常,生命周期与现有值相关联。典型的例子是这样的:

fn search<'a, 'b>(haystack: &'a str, needle: &'b str) -> Option<&'a str>

...表示返回值将来自haystack,而不是来自needle。另一方面,fn run_me_2&lt;'a&gt;() 几乎没有用处,因为它不允许调用者根据它关心的值来选择有用的生命周期,而只是提供'static 作为唯一可能的名称。

如何修复 run_me_2 以便编译?

如果您坚持为Obj 设置一个named 生命周期,那么您需要引入一个函数来接收与该生命周期相关的值。例如,您可以将对变量的引用传递给内部函数:

fn run_me_2() {
    fn inner_fn<'a>(r: &'a mut i32) {
        let s: Obj<'a> = Obj {
            inside: r,
        };
        take_and_return(s);
    }
    let mut v = 42;
    inner_fn(&mut v);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-08-22
    • 2021-11-02
    • 2015-02-22
    • 2019-03-04
    • 2022-01-22
    • 2017-05-07
    • 1970-01-01
    • 2022-01-06
    相关资源
    最近更新 更多