我试图在Rust中创建一个简单的扫雷器。为此,我想创建一个Grid
对象,保存所有Case
对象。单击Case
,通知Grid
。
我希望所有的情况下有一个Weak
对网格的引用。为什么借用检查器阻止我这样做?
引用文档:
Weak
是Rc
的一个版本,它持有对托管分配的非归属引用。
注释标记的link
不应该拥有网格,但编译器告诉我该值已被移动。
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) -> Self {
let (n, m) = dimensions;
let mines: Vec<Vec<Case>> = Vec::with_capacity(m);
let mut grid = Self {
dimensions: dimensions,
n_mines: n_mines,
content: mines,
};
let link = Rc::new(grid);
let link = Rc::downgrade(&link); // here link loses the ownership of grid, right ?
println!("{}n", grid.n_mines);
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(&link));
line.push(case)
}
grid.content.push(line);
}
grid
}
pub fn click_neighbours(&mut self, coordinates: (usize, usize)) {
let surrouding = vec![(0, 0), (0, 1)]; // not a real implementation
for coord in surrouding {
self.content[coord.0][coord.1].click();
}
}
}
pub struct Case {
pub coordinates: (usize, usize),
parent: Weak<Grid>,
}
impl Case {
pub fn new_with_parent(coordinates: (usize, usize), parent: Weak<Grid>) -> Case {
Self {
coordinates: coordinates,
parent: parent,
}
}
pub fn click(&mut self) {
match self.parent.upgrade() {
Some(x) => x.click_neighbours(self.coordinates),
None => println!("whatever, not the issue here"),
}
}
}
fn main() {
let grid = Grid::new((10, 10), 5);
}
我通过将网格内容放入RefCell
,在运行时尝试了多种变体来执行借用规则。我目前也在尝试在Rust中采用Observer设计模式实现来解决我的问题,但到目前为止还没有任何成功。
这里有一个误解,通过移动这里的网格:
let link = Rc::new(grid);
你不可挽回地摧毁了grid
,但无论你对link
做什么,都不能唤回原来的grid
。唯一可以这样做的是使用Rc::try_unwrap
来打开link
并将其分配回grid
,但这不是你想在这里做的。
你可以用RefCell
来代替,像这样:
let grid = Rc::new(RefCell::new(grid));
然后让你编辑网格里面的borrow_mut
:
grid.borrow_mut().content.push(line);
你可以在PitaJ的回答中看到你的代码的完整清单应该是什么样子的。
-
返回grid
被移到了Rc::new
中,所以你不能再使用它了。你必须从Grid::new
Rc
-
当你持有
Weak
对它的引用时,你不能修改Rc
中的值。因此,您必须在RefCell
中包装内部值。否则,创建line
s的循环是不可能的
这是你的代码的有效版本:
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;
let mines: Vec<Vec<Case>> = Vec::with_capacity(m);
let grid = Rc::new(RefCell::new(Self {
dimensions: dimensions,
n_mines: n_mines,
content: mines,
}));
let link_weak = Rc::downgrade(&grid);
println!("{}n", grid.borrow().n_mines);
{
// avoid repeatedly calling `borrow_mut` in the loop
let grid_mut = &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(&link_weak));
line.push(case)
}
grid_mut.content.push(line);
}
}
grid
}
pub fn click_neighbours(&mut self, coordinates: (usize, usize)) {
let surrouding = vec![(0, 0), (0, 1)]; // not a real implementation
for coord in surrouding {
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>>) -> Case {
Self {
coordinates: coordinates,
parent: parent,
}
}
pub fn click(&mut self) {
match self.parent.upgrade() {
Some(grid) => grid.borrow_mut().click_neighbours(self.coordinates),
None => println!("whatever, not the issue here"),
}
}
}
fn main() {
let grid = Grid::new((10, 10), 5);
}
游乐场