关于 RUST 生命周期的疑问 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
liangzimo
V2EX    Rust

关于 RUST 生命周期的疑问

  •  
  •   liangzimo 2022-05-12 10:36:53 +08:00 3099 次点击
    这是一个创建于 1300 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这是代码

    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 是不一样的吗

    14 条回复    2022-05-12 13:53:55 +08:00
    czzhengkw
        1
    czzhengkw  
       2022-05-12 10:43:55 +08:00
    rust 新手,说一下自己的看法

    因为声明了 `get_name()` 返回的引用生命周期是 `name` 的生命周期,而不是 `obj` 的,而 `obj` 的构建是使用 `n` 的引用。编译器可以识别出 `n` 的生命周期能满足要求,所以不会报错
    liangzimo
        2
    liangzimo  
    OP
       2022-05-12 10:50:35 +08:00
    @czzhengkw 感谢,同新手,显示添加的'a 的代表 n 的生命周期吗,这个就很不好理解
    bearice
        3
    bearice  
       2022-05-12 10:53:57 +08:00
    fn get_name(&self) -> & str 返回的 lifetime 是 self 的, 而 self (就是 obj 这个变量)在 15 行就没了
    变量的生命周期和变量所代表的类型的生命周期是两回事儿
    bearice
        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
    liangzimo
        5
    liangzimo  
    OP
       2022-05-12 11:00:22 +08:00
    @bearice 那显示添加的'a 是 n 的生命周期吗,因为只有这样编译器才会通过检查吧,这两者是怎么关联上的
        6
    saltbo  
       2022-05-12 11:01:15 +08:00
    @bearice 意思是 self 的生命周期并不是伴随整个 struct 的吗 出了 method 的作用于就没了?
    bearice
        7
    bearice  
       2022-05-12 11:10:49 +08:00
    @liangzimo 不是 n 的,而是指示 n 所引用的对象的。
    就相当于你告诉编译器,保证类型 AA 中的 n 所引用的值在'a 这个生命周期内都有效。
    这个周期和 类型为 AA 的变量的生命周期 是两回事儿。
    Kilerd
        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
    Buges
        9
    Buges  
       2022-05-12 11:27:44 +08:00 via Android
    记住生命周期是泛型参数的一种。 &'a T 是 Ref<A,T>的语法糖。
    gydi
        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 的生命周期去推导出来,再检查
    liangzimo
        11
    liangzimo  
    OP
       2022-05-12 11:37:58 +08:00
    @bearice @Buges @Kilerd 感谢几位,理解了!
    @gydi 这个想法好,一下就通了
    libook
        12
    libook  
       2022-05-12 12:09:14 +08:00
    我也是初学者,尝试分析一下这个问题。

    生命周期第三条规则是默认应用的,你自己标注生命周期,就会覆盖这个默认声明,也就是说第三条规则不再自动应用,编译器会按照你标注的生命周期来检查生命周期。

    修改后,get_name 的输出生命周期与 AA 实例一样,AA 的输入生命周期与 n 一样,那么只要 n 还活着,你就可以用 get_name 的返回值。
    libook
        13
    libook  
       2022-05-12 12:27:03 +08:00
    @bearice #7 请问我如此理解是否正确:
    方法默认的输入生命周期实际上有两个,一个是&self ,另一个是方法所对应的类型(即本题目中的结构体),生命周期省略规则的第三条只是说方法输出值的生命周期默认和&self 一样,但也默认和方法所对应的类型不同。
    bearice
        14
    bearice  
       2022-05-12 13:53:55 +08:00
    @libook 这么说也没问题
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3007 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 13:34 PVG 21:34 LAX 05:34 JFK 08:34
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86