下面这一段rust代码,self.target的生命周期是'b而不是'a呢?
pub struct Writer<'a> {
target: &'a mut String,
}
impl<'a> Writer<'a> {
fn indent<'b>(&'b mut self) -> &'a String {
self.target
}
}
编译器报错:
error: lifetime may not live long enough
--> src/bin/main29.rs:19:9
|
17 | impl<'a> Writer<'a> {
| -- lifetime 'a
defined here
18 | fn indent<'b>(&'b mut self) -> &'a String {
| -- lifetime 'b
defined here
19 | self.target
| ^^^^^^^^^^^ method was supposed to return data with lifetime 'a
but it is returning data with lifetime 'b
|
= help: consider adding the following bound: 'b: 'a
1
共 4 条评论, 1 页
评论区
写评论我觉得这个彻底搞清楚了,生命周期也基本就差不多了。
首先是结论:
然后是为什么
首先, 生命周期分 liveness scope 与 lifetime, 这两个虽然比较类似,但不是一回事。
对于一个
let x = &T
来说, liveness 起始于定义, 中止于最后一次访问, 若还实现了 drop, 则中止于主动drop(T)
或者 containing block 中止。lifetime 就是常见的 'a 的东西。
关键: liveness scope 并不一定等于lifetime.
lifetime的取值一般是编译器自由确定的,编译器一般会将lifetime设置为与 liveness scope 相同的值。 比如,
let x = &'a T
, 编译器基本不会将 'a 设置为 x 的 liveness scope 不同的值。所以在感官上, 就造成了 x 存在, 在 'a 存在, x drop 了, 则 'a 也就不存在了的印象。但这个并非永远成立。
从另一个方面来说, liveness scope 与 'a 是编译期概念, 只有编译器会关心,并且一旦确定, 就不会改变。 编译器用他们确定是否编译失败。
回到原来的问题, 为啥 通过 &'a &'b mut T 这种形式, 无论你怎么做, 都无法获得 &'b T 或者 &'b mut T 呢?
因为 liveness scope 与 'a 并不总是一致, 因此, 存在一种可能。 继续用
let x = &'a T
这个例子, 那么就是 x 已经消失了, 但 'a 还在。 虽然在代码上你看不到它了, 但编译器看得到。在原来问题中, 你一旦获得 &'b T, 则编译器在 lifetime 'b 范围内, 会认为存在这个引用。 那么考虑这段代码
若允许通过某种方法获得引用 &'b T, 则最终编译会失败, 因为 s3 虽然没了, 但编译器认为存在一个看不见的引用, 这个引用生命期为 'b, 恰好 s1 持有的引用 lifetime 也是 'b, 这就违反了 mut ref 只能有一个实例时才能写的原则。
而这看起来很诡异, 因为从正常角度看来,
*s1 = String::from("2")
并没有违反引用的约定,不该编译不了。其余情况可以仿照上面分析, 最终就是上面的结论。
最后, 一个现实的例子:
--
👇
QingJuBaiTang: 如果把target声明称immutable &,就可以用返回'a,这是为什么
这是 NLL 针对重新借用添加约束的问题。
访问
self.target
时,其 supporting prefix 会根据target: &'a {mut} String
的类型而有所不同:对于
fn indent<'b>(&'b mut self) -> &'a String { self.target }
,函数返回对 target 字段的 reborrow,并且target: &'a mut String
,self.target
被 reborrow 时,其支撑前缀为self.target
和self
,在添加self
进来的时候,self
携带的'b
需要形成一个'b: 'a
约束 —— 这表明 reborrowself.target
需要保证&'b mut self
存活 —— 编译器告诉你需要这个约束而编译失败。target: &'a String
,self.target
被 reborrow 时,其支撑前缀仅为self.target
,因为对共享引用解引用时停止添加前缀,这意味着无需考虑self
(也就是&'b mut self
的'b
)存活,所以编译通过。--
👇
QingJuBaiTang: 如果把target声明称immutable &,就可以用返回'a,这是为什么
如果把target声明称immutable &,就可以用返回'a,这是为什么
因为传进来的self变成'b了。