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

Rust中Subtyping与Variance(&mut不变性)相关的两类编译疑问

问题解答

第一个问题:编译错误产生的原因

错误代码

fn main() {
    struct Foo<'a, T> {
        a: &'a mut T,
    }
    
    let p1 = 1;
    let mut p2 = &p1;
    {
        let p3 = 2;
        let mut p4 = &p3;
        let mut f = Foo {
            a: &mut p4,
        };
        f.a = &mut p2;
        println!("{}", f.a);
    }
    println!("{}", p2);
}

错误信息

error[E0597]: `p3` does not live long enough
  --> src/main.rs:10:19
   |
10 |         let mut p4 = &p3;
   |                      ^^^ borrowed value does not live long enough
...
16 |     }
   |     - `p3` dropped here while still borrowed
17 |     println!("{}", p2);
   |                    -- borrow later used here

原因分析

创建Foo实例f时,初始赋值a: &mut p4让编译器推断出Foo的生命周期参数'a至少要覆盖p4的生命周期(也就是内部代码块的范围)。虽然后续我们把f.a改成了&mut p2,但Rust的生命周期推断基于变量声明时的约束,而非后续赋值——f的'a已经被固定为内部代码块的生命周期,这意味着p2被f.a持有的可变引用会被限制在'a范围内。

最后一行println!("{}", p2)尝试使用p2,此时p2仍被f持有的引用(即便已被覆盖)借用,而f的生命周期绑定了p3的生命周期(初始赋值关联了p3),p3在内部代码块结束时就会销毁。编译器判定p2的借用延续到了p3销毁之后,违反了“引用不能超过被引用对象生命周期”的规则,因此报错。


第二个问题:注释最后一行后为何能编译成功

修改后代码

fn main() {
    struct Foo<'a, T> {
        a: &'a mut T,
    }
    
    let p1 = 1;
    let mut p2 = &p1;
    {
        let p3 = 2;
        let mut p4 = &p3;
        let mut f = Foo {
            a: &mut p4,
        };
        f.a = &mut p2;
        println!("{}", f.a);
    }
    // println!("{}", p2);
}

原因分析

注释掉最后一行后,p2在内部代码块之外不再被使用,编译器可以灵活调整生命周期推断:

  1. 虽然f初始绑定了p4的生命周期,但后续f.a被替换为&mut p2,且外部不再引用p2,编译器会将Foo的生命周期参数'a缩小到内部代码块范围。
  2. 关于&'a mut T的不变性:这里的不变性针对的是T的类型本身,而非T内部的生命周期。我们修改的是f.a指向的&i32的生命周期(从p3的短生命周期变为p1的长生命周期),而非T的类型(T始终是&i32)。而&i32本身是协变的,因此&'a mut &'long i32可以安全赋值给期望&'a mut &'short i32的位置(只要'long覆盖'a),不会触发不变性限制。
  3. 内部代码块结束后,f和p3都会被销毁,所有借用都在被引用对象销毁前结束,符合Rust的借用规则,因此编译通过。

内容的提问来源于stack exchange,提问作者dd.ho

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 23:50:24