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

Rust中self相关借用错误的困惑及解决方案咨询

Rust借用检查错误分析与解决方案

简化代码示例(MRE)

use std::collections::HashMap;

fn main() {
    let mut framework = Framework {
        index_list: Vec::new(),
    };
    framework.method_one();
}

#[derive(Debug)]
struct Framework {
    index_list: Vec<usize>,
}

#[derive(Debug)]
struct Doc<'a> {
    framework: &'a Framework,
}

impl Framework {
    fn method_one(&mut self){
        println!("method one");
        let index_list: Vec<usize>;
        let (doc_map, index_list) = self.get_map_and_list();
        println!("map {:#?}, list {:#?}", doc_map, self.index_list);
        self.index_list = index_list;
        // uncommenting this causes the issue:
        // self.manipulate_list_more(doc_map);
    }
    
    fn get_map_and_list(&self) -> (HashMap<String, Doc>, Vec<usize>) {
        let mut doc_map = HashMap::new();
        doc_map.insert("bubble".to_string(), Doc{framework: &self});
        let mut index_list: Vec<usize> = Vec::new();
        index_list.push(99);
        (doc_map, index_list)
    }
    
    fn manipulate_list_more(&mut self, doc_map: HashMap<String, Doc>) {
        // NB whether this is "&self" or "&mut self" is irrelevant ...
        println!("submethod_two");
        // even if this is commented out we still get the error...
        // showing that the commands in this method are irrelevant to the problem involved
        // self.index_list.push(100);
    }
}

问题背景

这是业务代码的简化版本,Doc必须持有Framework的引用,因此使用了生命周期参数。原本希望将map和list都作为Framework的字段,但Framework是pyclass,不允许携带生命周期参数,所以只能采用当前结构。取消注释self.manipulate_list_more(doc_map)后,触发以下借用检查错误:

error[E0506]: cannot assign to `self.index_list` because it is borrowed
  --> src\main.rs:28:3
   |
26 |         let (doc_map, index_list) = self.get_map_and_list();
   |                                     ----------------------- `self.index_list` is borrowed here
27 |         println!("map {:#?}, list {:#?}", doc_map, self.index_list);
28 |         self.index_list = index_list;
   |         ^^^^^^^^^^^^^^^ `self.index_list` is assigned to here but it was already borrowed
29 |         self.manipulate_list_more(doc_map);
   |                                   ------- borrow later used here

疑问解答

1. 为什么从get_map_and_list返回后,self的借用并未结束?

get_map_and_list返回的HashMap<String, Doc>中,每个Doc实例都持有&self的不可变引用。根据Rust的生命周期规则,这个引用的生命周期与get_map_and_list的&self参数绑定——只要doc_map还处于作用域内(即还被使用),self的不可变借用就会持续生效。

2. 注释掉self.manipulate_list_more(doc_map)后,编译器允许对self.index_list赋值的原因?

这是Rust 非 lexical lifetimes(NLL) 优化的结果。当你不把doc_map传给后续方法时,编译器能分析出doc_map在执行self.index_list = index_list之前已经不再被使用(唯一的使用是之前的println!),因此会提前结束self的不可变借用,允许后续对self.index_list的可变操作(赋值需要可变借用)。但如果要将doc_map传入manipulate_list_more,说明doc_map在赋值之后仍会被使用,借用无法提前结束,可变借用和之前的不可变借用就会产生冲突,触发报错。


可行解决方案

结合Framework是pyclass、不能携带生命周期的限制,推荐以下几种方案:

方案1:使用Rc<RefCell<Framework>>实现内部可变性

将Framework包裹在Rc<RefCell>中,让Doc持有Rc<RefCell<Framework>>,这样既不需要生命周期参数,又能实现对Framework的内部可变访问,同时满足Doc持有Framework引用的需求:

use std::collections::HashMap;
use std::rc::Rc;
use std::cell::RefCell;

fn main() {
    let framework = Rc::new(RefCell::new(Framework {
        index_list: Vec::new(),
    }));
    framework.borrow_mut().method_one(Rc::clone(&framework));
}

#[derive(Debug)]
struct Framework {
    index_list: Vec<usize>,
}

#[derive(Debug)]
struct Doc {
    framework: Rc<RefCell<Framework>>,
}

impl Framework {
    fn method_one(&mut self, framework_rc: Rc<RefCell<Framework>>){
        println!("method one");
        let (doc_map, index_list) = self.get_map_and_list(framework_rc);
        println!("map {:#?}, list {:#?}", doc_map, self.index_list);
        self.index_list = index_list;
        self.manipulate_list_more(doc_map);
    }
    
    fn get_map_and_list(&self, framework_rc: Rc<RefCell<Framework>>) -> (HashMap<String, Doc>, Vec<usize>) {
        let mut doc_map = HashMap::new();
        doc_map.insert("bubble".to_string(), Doc{framework: framework_rc});
        let mut index_list: Vec<usize> = Vec::new();
        index_list.push(99);
        (doc_map, index_list)
    }
    
    fn manipulate_list_more(&mut self, doc_map: HashMap<String, Doc>) {
        println!("submethod_two");
        // 可以安全修改index_list
        self.index_list.push(100);
    }
}

注意:在Python绑定场景中,需要确保Rc和RefCell与Python的GIL兼容,通常可以通过pyo3的相关工具处理。

方案2:使用Weak<RefCell<Framework>>避免循环引用

如果Doc不需要始终持有Framework的强引用,可以改用Weak指针,防止潜在的循环引用问题:

use std::collections::HashMap;
use std::rc::{Rc, Weak};
use std::cell::RefCell;

#[derive(Debug)]
struct Doc {
    framework: Weak<RefCell<Framework>>,
}

// 其余代码类似方案1,get_map_and_list中传入Weak::clone(&framework_weak)即可

使用时需要通过upgrade()方法将Weak转为Rc,注意处理None的情况(如果Framework已被销毁)。

方案3:重构数据依赖(业务允许时)

如果业务逻辑允许,将Doc需要的Framework数据单独提取出来,让Doc持有具体数据而非整个Framework的引用,从根源上避免生命周期冲突。比如:

#[derive(Debug)]
struct Doc {
    // 只保存需要的字段,比如index_list的快照或特定数据
    some_data: Vec<usize>,
}

这种方案最简洁,但需要根据实际业务场景评估可行性。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 15:09:53