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

为何持有不可变引用的结构体仍触发可变借用冲突?

Rust借用报错分析与解决

问题代码

#[derive(Debug)]
struct ExternalStruct {
    some_thing: i32,
}

impl ExternalStruct {
    fn non_mutable_read(&self) {
        println!("{}", self.some_thing);
    }

    fn create(&mut self) -> MyStruct {
        self.some_thing += 1;
        MyStruct{external_value: self}
    }
}

#[derive(Debug)]
struct MyStruct<'a> {
    external_value: &'a ExternalStruct,
}


fn main() {
    let mut ext = ExternalStruct { some_thing: 18 };
    let my = ext.create();
    ext.non_mutable_read();
    println!("{:?}", my);
}

报错信息

error[E0502]: cannot borrow `ext` as immutable because it is also borrowed as mutable
  --> src\main.rs:26:5
   |
25 |     let my = ext.create();
   |              --- mutable borrow occurs here
26 |     ext.non_mutable_read();
   |     ^^^ immutable borrow occurs here
27 |     println!("{:?}", my);
   |                      -- mutable borrow later used here

疑问

原本以为只有当MyStruct持有&mut ExternalStruct时才会触发这类错误,但实际它持有的是不可变引用,无法理解借用失败的原因。认为create方法内的可变借用会在方法结束时结束,且MyStruct的不可变引用应该能和non_mutable_read的不可变借用兼容。

原因分析

问题核心在于create方法的参数是&mut self——Rust会将这个可变借用的生命周期与返回值MyStruct的生命周期'a绑定。也就是说,只要my这个MyStruct实例还存在,ext的可变借用就一直有效。

而Rust的借用规则明确:可变引用与任何其他引用(包括不可变引用)互斥。所以在my存活期间,你无法再对ext进行不可变借用(调用non_mutable_read),哪怕MyStruct里存的是不可变引用也不行——因为最初的可变借用还没被释放。

解决方案

有两种常见的修复方式:

方式1:拆分可变操作与引用返回

把create方法拆成两步:先完成可变修改,再返回不可变引用。这样可变借用的范围会被限制在修改操作阶段,不会被延长到返回值:

impl ExternalStruct {
    fn non_mutable_read(&self) {
        println!("{}", self.some_thing);
    }

    fn increment(&mut self) {
        self.some_thing += 1;
    }

    fn create(&self) -> MyStruct {
        MyStruct{external_value: self}
    }
}

// main函数修改为:
fn main() {
    let mut ext = ExternalStruct { some_thing: 18 };
    ext.increment();
    let my = ext.create();
    ext.non_mutable_read();
    println!("{:?}", my);
}

方式2:在方法内缩小可变借用范围

如果不想拆分方法,可以通过显式转换借用类型,让编译器识别到可变借用已经结束:

impl ExternalStruct {
    fn non_mutable_read(&self) {
        println!("{}", self.some_thing);
    }

    fn create(&mut self) -> MyStruct {
        // 先完成可变操作,此时可变借用仅作用于这一步
        self.some_thing += 1;
        // 显式借用不可变的self,此时之前的可变借用已释放
        MyStruct{external_value: &*self}
    }
}

不过这种方式依赖编译器的生命周期推断,复杂场景下拆分方法会更稳妥。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 01:45:19