通用生命周期是否体现为引用的生命周期还是引用值的生命周期?

3

请考虑以下程序:

fn main() {
    let c;                                      |<-'a
    let mut a = "Hello, world!".to_string();    |
    {                                           |
        let b = &mut a;           |<-'b         |
        c = foo(b);               |             |
    }                                           |
    println!("{}", c)                           |
}

fn foo<'z>(a: &'z mut str) -> &'z str {
    a
}

b的生命周期是'b',但c的生命周期是'a',比'b'的生命周期更长。 foo的生命周期约束要求foo的返回值(在这种情况下为c)应该具有与其参数(在这种情况下为b)相同的生命周期。那么如何满足foo的生命周期约束呢?

然而,这个程序可以编译通过,所以我猜测foo的生命周期参数'z'会体现为b引用值(a)的生命周期,以便满足foo的生命周期约束?


似乎你已经回答了自己的问题。你还期待什么? - SOFe
我只是想确认一下我的逻辑和猜测是否正确,还有我的猜测是否存在任何例外情况? - Helin Wang
1
b 的类型为 &'a mut str。引用的生命周期将是拥有该值的作用域。在这种情况下,您没有将 a 移动到 'b 中,因此您的引用仍然是 &'a。但是,如果您将 &b 传递给 foo(即使类型匹配),您将会得到错误,因为 b 存在于 'b 内部。 - SOFe
1
借用表达式的生命周期不一定与任何变量的生命周期相同。编译器不必从输入生命周期中选择输出生命周期,它可以创建一个“新”的生命周期,以满足任何出现借用表达式的要求。(还要注意foo(b)进行了隐式重新借用,因此c的类型和b的类型不需要具有相同的生命周期参数) - trent
1个回答

2

一个值有自己的生命周期,但是引用还会跟踪它所引用的东西的生命周期。不幸的是,在这里使用官方术语上存在一定的缺乏。我(以及一些其他人)开始使用的术语是具体生命周期。在main函数中有三个变量,因此有三个具体生命周期:


fn main() {
    let c;                     //       'c
    let mut a = String::new(); // 'a     ¦
    {                          //  |     ¦
        let b = &mut a;        //  | 'b  ¦
        c = foo(b);            //  |  |  |
    }                          //  |     |
    println!("{}", c)          //  |     |
}
a 是一个 Stringb 是一个 &mut Stringc 是一个 &str。这三个变量都是值,但是 bc 也是引用。在这种情况下,b 引用了 a 中的值,并且是 &'a mut String。由于 c 源自于 b,它有相同的 "内部生命周期":&'a str

值得注意的是,b 本身的生命周期从未发挥作用。这极为罕见,因为你需要可变借用和 "额外" 借用:

fn main() {
    let c;
    let mut a = String::new();
    {
        let mut b = &mut a;
        c = foo(&mut b);    // Mutably borrowing `b` here
    }
    println!("{}", c)
}

error[E0597]: `b` does not live long enough
 --> src/main.rs:6:17
  |
6 |         c = foo(&mut b);
  |                 ^^^^^^ borrowed value does not live long enough
7 |     }
  |     - `b` dropped here while still borrowed
8 |     println!("{}", c)
  |                    - borrow later used here

在这种情况下,传递给 foo 的值的类型为 &'b mut &'a mut String,它被强制转换为 &'b mut str。 值 b 的寿命不够长,因此您会收到错误信息。

我认为这个模型不能解释更复杂的借用关系。例如,在 println! 之后再次使用 a,可变借用就不能是整个 a 的生命周期。

a 的可变借用由 c 持有,但是借用的持续时间不需要与 c 的生命周期相对应。由于非词法生命周期(在这种情况下最好称为“非词法借用”),由 c 持有的 a 的借用可以在 println! 之后终止,但在作用域结束之前。
将上述图表改进以显示值的生命周期以及所引用值的生命周期(括号内):
fn main() {
    let c;                     //           'c
    let mut a = String::new(); // 'a         ¦
    {                          //  |         ¦
        let b = &mut a;        //  | 'b('a)  ¦
        c = foo(b);            //  |  |('a)  |('a)
    }                          //  |         |('a)
    println!("{}", c);         //  |         |('a)
                               //  |         |
    println!("{}", a);         //  |         |
}

另请参阅:

Original Answer翻译成"最初的回答"


我认为这个模型无法解释更复杂的借用关系。例如,在println!之后再次使用a,可变借用不能涵盖整个a的生命周期。 - trent
更好。正如您所提到的,我们正在处理术语上的不精确性。虽然它仍然与我的心理模型不匹配,但我可能过度拟合了。 - trent

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