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

Rust中使用Rc::downgrade为何仍触发所有权移动错误?

解决Rust扫雷游戏中Case持有Grid Weak引用的所有权问题

问题根源分析

你的代码存在两个核心问题:

  1. 所有权移动导致变量失效:执行let link = Rc::new(grid);时,grid的所有权已完全转移到Rc实例中,后续再访问原grid变量(比如读取n_mines或修改content)属于非法操作——原变量已被移动,不再拥有所有权。
  2. 不可变引用无法调用可变方法:即使解决了所有权问题,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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 21:05:55