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

为何Rust允许同时持有同一对象的可变与不可变引用(仅使用其一)?

Rust可变与不可变引用共存的编译规则解释

先看你提到的两段代码:

编译通过的代码

fn main() {
    let mut str_1 = String::from("hello");
    let ref_1 = &str_1; // 创建不可变引用
    let ref_2 = &mut str_1; // 创建可变引用
    println!("{}", ref_2); // 仅使用可变引用
}

编译失败的代码

fn main() {
    let mut str_1 = String::from("hello");
    let ref_1 = &str_1;
    let ref_2 = &mut str_1;
    println!("{}", ref_2);
    println!("{}", ref_1); // 同时使用两个引用,触发编译错误
}

问题一:是否只要不使用ref_1就可编译?

是的,只要没有同时活跃使用不可变引用ref_1和可变引用ref_2,代码就能通过编译。

Rust 1.31之后引入的非词法生命周期(Non-Lexical Lifetimes, NLL)让借用检查器不再以代码块为单位判断生命周期,而是精准追踪每个引用的最后一次使用位置。在第一段代码中,ref_1创建后完全没被使用,编译器会认为它的生命周期在ref_2创建前就已经结束,不会和可变引用产生冲突,因此允许编译。

哪怕你使用了ref_1,只要它的最后一次使用早于可变引用的创建,同样能编译:

fn main() {
    let mut str_1 = String::from("hello");
    let ref_1 = &str_1;
    println!("{}", ref_1); // ref_1的最后一次使用
    let ref_2 = &mut str_1; // 此时ref_1已不再活跃,允许创建可变引用
    println!("{}", ref_2);
}

问题二:为何Rust允许这种仅使用其一的引用共存情况?

这是NLL带来的灵活性优化,核心是在保证内存安全的前提下,让代码更自然易读,减少不必要的嵌套。

在没有NLL的旧版Rust中,引用的生命周期绑定到代码块,你必须手动用代码块分隔不同引用的使用范围:

// 旧版Rust的写法,需要额外嵌套代码块
fn main() {
    let mut str_1 = String::from("hello");
    {
        let ref_1 = &str_1;
        println!("{}", ref_1);
    } // 代码块结束,ref_1生命周期终止
    let ref_2 = &mut str_1;
    println!("{}", ref_2);
}

NLL让编译器能智能判断引用的实际活跃区间,不需要手动加代码块。Rust的核心规则从来不是“不能同时创建可变和不可变引用”,而是“不能同时活跃使用可变和不可变引用”——创建引用但不使用,不会触发数据竞争或内存安全问题,所以编译器允许这种写法,既守住了安全底线,又让代码编写更顺畅。

内容的提问来源于stack exchange,提问作者gANDALF

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 06:22:40