这是代码
fn main() {
let n = String::from("rust");
let myname;
{
let obj = AA{
name: &n
};
myname = obj.get_name();
}
println!("{}", myname)
}
struct AA<'a>{
name: &'a str
}
impl <'a> AA<'a>{
fn get_name(&self) -> & str{
return &self.name;
}
}
按照文档来说,下面的报错能理解,因为 myname 的生命周期应该和 obj 是一样的,出了作用范围就被编译器检查出来了
error[E0597]: `obj` does not live long enough
--> src/main.rs:14:18
|
14 | myname = obj.get_name();
| ^^^^^^^^^^^^^^ borrowed value does not live long enough
15 | }
| - `obj` dropped here while still borrowed
16 | println!("{}", myname)
| ------ borrow later used here
但是,如果我把方法返回值写明返回的生命周期,如下
impl <'a> AA<'a>{
fn get_name(&self) -> &'a str{
return &self.name;
}
}
这样能正常运行
warning: `hello_cargo` (bin "hello_cargo") generated 4 warnings
Finished dev [unoptimized + debuginfo] target(s) in 0.45s
Running `target/debug/hello_cargo`
rust
疑问:为什么会产生这样的区别,按照生命周期省略规则的第三条规则来说默认的返回生命周期是&self 的,也就是 obj 的,这个生命周期和'a 是不一样的吗
1
czzhengkw 2022-05-12 10:43:55 +08:00
rust 新手,说一下自己的看法
因为声明了 `get_name()` 返回的引用生命周期是 `name` 的生命周期,而不是 `obj` 的,而 `obj` 的构建是使用 `n` 的引用。编译器可以识别出 `n` 的生命周期能满足要求,所以不会报错 |
3
bearice 2022-05-12 10:53:57 +08:00
fn get_name(&self) -> & str 返回的 lifetime 是 self 的, 而 self (就是 obj 这个变量)在 15 行就没了
变量的生命周期和变量所代表的类型的生命周期是两回事儿 |
4
bearice 2022-05-12 10:56:57 +08:00 2
fn get_name(&self) -> & str
写成这样也许更容易理解 fn get_name(self : &'b AA<'a> ) -> &'b str |
7
bearice 2022-05-12 11:10:49 +08:00
@liangzimo 不是 n 的,而是指示 n 所引用的对象的。
就相当于你告诉编译器,保证类型 AA 中的 n 所引用的值在'a 这个生命周期内都有效。 这个周期和 类型为 AA 的变量的生命周期 是两回事儿。 |
8
Kilerd 2022-05-12 11:25:06 +08:00 2
@bearice 说的没错,具体分析一下:
``` 1 fn main() { 2 let n = String::from("rust"); 3 let myname; 4 { 5 let obj = AA{ 6 name: &n 7 }; 8 myname = obj.get_name(); 9 } 10 println!("{}", myname) 11 12 } ``` n 的生命周期是 2-12 myname 是从 3-12 obj 的是 5-9 fn get_name(&self) -> & str 这个代码为什么当前版本能运行,是因为 Rust 为了简化生命周期的繁琐写法,给出三条自动补全规则,这里命中其中一条「如果 parameter 和 return value 有且仅有一个 ref ,便给他自动补上一个匿名的 lifetime 」 也就是。fn get_name(&'_ self) -> &'_ str 。 值得注意的是这里的 _ 并不是你在结构体上写的 'a ,所以在经历了某个阶段之后会变成 @bearice 说的 fn get_name(&'ccc self) -> &'ccc str 。而这个 ccc 是来自于 struct ref 的,所以生命周期只能是遵循 obj 的生命周期,所以是 5-9 当你写成 fn get_name(& self) -> &'a str 的时候发生了什么呢? 注意的是 'a 是来自于 field name 的,name 的值 &n 的生命周期是 n 的生命周期,即 2-12 , 也就是说 小生命周期(5-9)的 obj 接纳了一个长生命(2-12)的 &n ,这显然是允许的 fn get_name(& self) -> &'a str 这种写法就是明确的告诉愚蠢的 rustc ,我返回的值是 ‘a 那么长的,别瞎搞我。 然后 rustc 你看 返回值 'a >= &n 的 ‘a ,ok ,pass , 这就返回了一个 ’a 的 &&n |
9
Buges 2022-05-12 11:27:44 +08:00 via Android
记住生命周期是泛型参数的一种。 &'a T 是 Ref<A,T>的语法糖。
|
10
gydi 2022-05-12 11:29:43 +08:00
好像可以这样理解
struct AA<'a> 这个生命周期不是定义 AA 实例的,而是定义具体用到该生命周期的属性的。 和#4 提到的一样 fn get_name(&self) -> &str 等价于 fn get_name(&'b self) -> &'b str 而 fn get_name(&self) -> &'a str 等价于 fn get_name<'t>(&'t self) -> &'a str where 'a : 't 这时'a 就可以由 AA 声明时传入的 name 的生命周期去推导出来,再检查 |
12
libook 2022-05-12 12:09:14 +08:00
我也是初学者,尝试分析一下这个问题。
生命周期第三条规则是默认应用的,你自己标注生命周期,就会覆盖这个默认声明,也就是说第三条规则不再自动应用,编译器会按照你标注的生命周期来检查生命周期。 修改后,get_name 的输出生命周期与 AA 实例一样,AA 的输入生命周期与 n 一样,那么只要 n 还活着,你就可以用 get_name 的返回值。 |