You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中生命周期与(重)借用、解引用的交互机制及代码场景疑问解析

Rust中生命周期与(重)借用、解引用的交互机制及代码场景疑问解析

作为有C/C++背景的开发者,刚接触Rust的生命周期和借用规则确实容易头大——毕竟这是Rust最独特也最核心的安全机制之一,和我们之前习惯的内存模型完全不一样。我来帮你拆解你代码里的各个场景,把底层的规则讲明白。

先把你的代码贴出来方便对照:

struct NotCopyable {}
struct B<'a> {
    b: &'a NotCopyable,    // 可Copy
    c: &'a mut NotCopyable, // 不可Copy
    d: Option<&'a mut NotCopyable>, // 不可Copy
}

impl<'a> B<'a> {
    fn some_f<'s>(&'s mut self) {
        // 1. 编译通过
        let _b: &'a NotCopyable = &(*(self.b));
        // 2. 编译器报错:`lifetime may not live long enough`
        // let _c: &'a mut NotCopyable = &mut (*(self.c));
        // 3. 编译通过
        let _c: &'s mut NotCopyable = &mut (*(self.c));
        // 4. 编译通过
        let _d: &'a mut NotCopyable = &mut *(self.d.take().unwrap());
    }
}

逐个场景拆解

场景1:不可变引用的解引用+取引用

let _b: &'a NotCopyable = &(*(self.b));
这里self.b是&'a NotCopyable,解引用*self.b得到NotCopyable的临时值,再取引用&得到新的引用。为什么新引用的生命周期可以是'a?
原因很简单:不可变引用是可Copy的,而且Rust允许对同一个对象存在多个不可变引用。编译器在这里其实是直接复用了原引用的生命周期'a——你写的&(*self.b)本质上和直接复制self.b是等价的,完全安全,所以编译器允许你把它绑定到&'a类型的变量上。

场景2:错误的可变引用重借用

let _c: &'a mut NotCopyable = &mut (*(self.c));
这行报错的核心是可变引用的重借用规则。首先,self是&'s mut self——也就是我们当前持有一个生命周期为's的B<'a>的可变借用。当你对self.c(&'a mut NotCopyable)执行&mut (*...)时,这不是直接复制(因为可变引用不可Copy),而是触发了重借用。

重借用的关键规则是:通过外层可变借用创建的新可变引用,生命周期必须被限制为外层借用的生命周期(也就是's),而不能回到原引用的'a。为什么?因为如果允许你创建一个'a生命周期的可变引用,就会出现两个指向同一个对象的可变引用(原self.c的&'a mut和新的&'a mut),这直接违反了Rust的“可变引用独占”规则。编译器报错“lifetime may not live long enough”,本质是说你试图把一个短生命周期('s)的引用赋值给长生命周期('a)的变量,而's必然比'a短(因为self是B<'a>的借用,'s是这个借用的有效期,肯定被'a包含)。

场景3:合法的可变引用重借用

let _c: &'s mut NotCopyable = &mut (*(self.c));
这行能通过,就是完全符合了可变重借用的规则。编译器自动帮你做了以下操作:

  1. 识别到你通过&'s mut self访问内部的&'a mut NotCopyable
  2. 自动创建一个新的可变引用,将其生命周期限制为's(和外层self的借用周期一致)
  3. 同时暂时“冻结”原self.c的&'a mut引用,直到这个's生命周期的重借用结束——这样就保证了在's期间,只有这个新的可变引用能访问NotCopyable对象,满足独占性要求。

说白了,这个过程就是编译器帮你把原本长生命周期的可变引用,临时“缩小”成当前借用的生命周期,确保安全。

场景4:转移引用后的重包装

let _d: &'a mut NotCopyable = &mut *(self.d.take().unwrap());
这里的关键是take()方法——它会把Option里的&'a mut NotCopyable取出来,同时把self.d变成None。也就是说,原结构体字段已经不再持有这个引用了,这个引用完全转移到了当前的局部作用域中。这时候你解引用再取引用,只是对这个原本就是'a生命周期的可变引用做了一次重新包装,没有触发重借用(因为没有通过外层借用去访问引用,而是直接拿到了引用本身),所以自然可以绑定到&'a mut类型的变量上。

重借用的核心规则总结

当多个生命周期交织时,重借用的规则可以归纳为:

  • 不可变引用:重借用的生命周期可以选择原引用的生命周期,或者外层借用的生命周期——因为不可变引用允许多个共享,两种选择都安全。
  • 可变引用:重借用的生命周期必须绑定到外层借用的生命周期,不能超过这个范围。这是为了保证可变引用的独占性,避免出现多个可变引用指向同一对象的情况。
  • 重借用是编译器自动触发的机制,不需要你手动声明,它的核心目的是在保证内存安全的前提下,让你可以灵活地使用嵌套的引用。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 13:13:07