【问题标题】:Does a generic lifetime materialize as the reference's lifetime or the referenced value's lifetime?通用生命周期是作为引用的生命周期还是被引用值的生命周期实现?
【发布时间】:2019-12-18 17:47:32
【问题描述】:

考虑以下程序:

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 的生命周期约束?

【问题讨论】:

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

标签: rust lifetime


【解决方案1】:

值有自己的生命周期,但引用也跟踪它所引用的事物的生命周期。不幸的是,这里缺少官方术语。我(和其他一些人)开始使用的术语是具体的生命周期。 main 中有三个变量,因此有三个具体的生命周期:

fn main() {
    let c;                     //       'c
    let mut a = String::new(); // 'a     ¦
    {                          //  |     ¦
        let b = &mut a;        //  | 'b  ¦
        c = foo(b);            //  |  |  |
    }                          //  |     |
    println!("{}", c)          //  |     |
}

aStringb&amp;mut Stringc&amp;str。所有三个变量都是值,但bc 也是 引用。这里b指的是a中的值,是&amp;'a mut String。由于c 派生自b,因此它具有相同的“内部生命周期”:&amp;'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 的值是&amp;'b mut &amp;'a mut String 类型,它被强制转换为&amp;'b mut strb 的值不够长,您会收到错误消息。

我认为这种模型无法解释更复杂的借贷关系。如果aprintln!之后再次使用,例如,可变借用不能持续a的整个生命周期

a 的可变借用由c 持有,但借用的持续时间不需要与c 的生命周期相对应。由于non-lexical lifetimes(在这种情况下更好地称为“非词法借用”),c 持有的a 的借用可以在println! 之后但在作用域结束之前终止。

增强上面的图表以显示 value 的生命周期以及括号中 referred-to value 的生命周期:

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

另见:

【讨论】:

  • 我认为这个模型不能解释更复杂的借贷关系。例如,如果在println! 之后再次使用a,则可变借用不能持续a 的整个生命周期。
  • 更好。正如您所提到的,我们正在处理术语缺乏精确性的问题。它仍然不符合我自己的心智模型,但我可能过度拟合。
猜你喜欢
  • 2015-11-18
  • 2016-04-11
  • 2014-09-10
  • 2021-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-06
相关资源
最近更新 更多