Rust中使用Rc::downgrade为何仍触发所有权移动错误?
解决Rust扫雷游戏中Case持有Grid Weak引用的所有权问题
问题根源分析
你的代码存在两个核心问题:
- 所有权移动导致变量失效:执行
let link = Rc::new(grid);时,grid的所有权已完全转移到Rc实例中,后续再访问原grid变量(比如读取n_mines或修改content)属于非法操作——原变量已被移动,不再拥有所有权。 - 不可变引用无法调用可变方法:即使解决了所有权问题,
Rc<Grid>只能提供不可变引用,而click_neighbours是&mut self的可变方法,直接调用会触发编译错误,需要结合RefCell实现内部可变性。
修正方案
我们用Rc<RefCell<Grid>>同时满足共享所有权和内部可变的需求,让Case持有Weak<RefCell<Grid>>引用,具体代码如下:
use std::cell::RefCell; use std::rc::{Rc, Weak}; pub struct Grid { dimensions: (usize, usize), n_mines: usize, content: Vec<Vec<Case>>, } impl Grid { pub fn new(dimensions: (usize, usize), n_mines: usize) -> Rc<RefCell<Self>> { let (n, m) = dimensions; // 先创建空Grid并包裹进Rc<RefCell> let grid = Rc::new(RefCell::new(Self { dimensions, n_mines, content: Vec::with_capacity(m), })); // 生成Weak引用供Case持有 let grid_weak = Rc::downgrade(&grid); // 通过RefCell获取可变引用,填充content let mut grid_mut = grid.borrow_mut(); for i in 0..m { let mut line: Vec<Case> = Vec::with_capacity(n); for j in 0..n { let case = Case::new_with_parent((i, j), Weak::clone(&grid_weak)); line.push(case); } grid_mut.content.push(line); } grid } pub fn click_neighbours(&mut self, coordinates: (usize, usize)) { let surrounding = vec![(0, 0), (0, 1)]; // 示例周边坐标 for coord in surrounding { // 增加坐标合法性检查,避免越界 if coord.0 < self.dimensions.1 && coord.1 < self.dimensions.0 { self.content[coord.0][coord.1].click(); } } } } pub struct Case { pub coordinates: (usize, usize), parent: Weak<RefCell<Grid>>, } impl Case { pub fn new_with_parent(coordinates: (usize, usize), parent: Weak<RefCell<Grid>>) -> Self { Self { coordinates, parent, } } pub fn click(&mut self) { match self.parent.upgrade() { Some(grid_rc) => { // 获取Grid可变引用并调用方法 let mut grid = grid_rc.borrow_mut(); grid.click_neighbours(self.coordinates); } None => println!("Grid已被销毁"), } } } fn main() { let grid = Grid::new((10, 10), 5); // 示例:点击左上角的Case grid.borrow_mut().content[0][0].click(); }
关键修改说明
- 用
Rc<RefCell<Grid>>替代直接返回Grid:既允许多个Case通过Weak引用共享Grid所有权,又能通过RefCell在运行时检查可变借用,绕过编译期的不可变限制。 - 先创建Rc包裹的Grid再填充内容:避免所有权移动后无法修改原Grid的问题,通过
borrow_mut()获取可变引用完成content的初始化。 - Case的parent类型改为
Weak<RefCell<Grid>>:upgrade后可通过borrow_mut()获取Grid的可变引用,调用click_neighbours方法。
注意事项
RefCell的借用检查是运行时的,若出现多个可变借用会触发panic,需确保逻辑中不会同时持有多个Grid的可变引用。- 实际开发中要完善坐标合法性检查,避免访问
content时出现索引越界错误。
内容的提问来源于stack exchange,提问作者timete_os
相关产品推荐
相关产品推荐

