我正在用Rust编写一个扫雷器实现.目前,它由enum Cell
组成,表示场的一个像元
#[derive(Copy, Clone, Debug)]
pub enum Cell {
Mine,
Flag,
Empty(u8),
}
和代表字段本身的struct Feild
#[derive(Debug)]
pub struct Field(Vec<Vec<Cell>>);
因此,从根本上说,该场只是一个包含一个元素的元组:包含单元的矢量的矢量.
目前,Field
struct 只有一个实现:
impl Field {
pub fn new(width: u8, height: u8, number_of_mines: u16) -> Result<Self, ()> { ... }
pub fn get_size(&self) -> (u8, u8, u16) { ... }
fn insert_mines(mut self, number_of_mines: u16) -> Result<Self, ()> { ... }
}
通过调用,关联函数new
允许创建Field
的实例.get_size
方法返回字段的维度.私有insert_mines
方法将指定数量的地雷随机添加到田地中.
main()
函数如下所示
fn main() {
let field = field::Field::new(9, 9, 9).unwrap();
println!("{}", field)
}
我正在创建一个9 x 9的字段,其中包含9个地雷,然后打印该字段(Field
和Cell
都正确实现了Display
个特征,但我不会在这里展示它,因为这不是我们的兴趣所在).
创建实例时,调用与new
关联的函数:
pub fn new(width: u8, height: u8, number_of_mines: u16) -> Result<Self, ()> {
let mut field = Field(vec![vec![Cell::Empty(0); width as usize]; height as usize]);
field = field.insert_mines(number_of_mines)?;
Ok(field)
}
它随后调用insert_mines
方法.Here it goes
fn insert_mines(mut self, number_of_mines: u16) -> Result<Self, ()> {
// get the width of a single row and the total cells number of the field
let (width, _, total_number_of_cells) = self.get_size();
// return an error if the required number of mines is less than 1 or is more than the total number of cells
// minus 1 (there should always be at least one cell without a mine)
if number_of_mines < 1 || number_of_mines > (total_number_of_cells - 1) {
return Err(());
}
// flatten the field for an easier interaction with it
let mut flattened_field = self.0.into_iter().flatten().collect::<Vec<Cell>>();
flattened_field.splice(
0..number_of_mines as usize,
(0..number_of_mines)
.map(|_| Cell::Mine)
.collect::<Vec<Cell>>(),
);
// shuffle the flattened field to randomly distribute the mines
let mut rng = thread_rng();
flattened_field.shuffle(&mut rng);
// convert the flattened fields back into its original `Vec<Vec<u16>>` form
self.0 = flattened_field
.chunks(width as usize)
.map(|chunk| chunk.to_vec())
.collect::<Vec<Vec<Cell>>>();
Ok(self)
}
它的工作原理如下:
- 将
Vec<Vec<Cell>>
展平为Vec<Cell>
(以便更容易使用) - 将前
n
个元素替换为Cell::Mine
个 - 对向量进行混洗
- 从
Vec<Cell>
重建Vec<Vec<Cell>>
并将结果设置为self.0
(因此使用新字段更新原始字段)
一切都运行正常.当该程序运行时,它会打印如下内容
□ □ □ * □ □ □ □ □
□ □ □ □ □ □ □ □ □
□ □ □ * □ □ □ □ □
□ □ □ □ * □ □ * □
* □ □ * □ □ □ □ □
□ □ □ □ * □ □ □ *
□ □ □ □ □ □ □ □ □
□ * □ □ □ □ □ □ □
□ □ □ □ □ □ □ □ □
这是完全正确的(正方形是空单元格,*
是地雷).
However,我不喜欢insert_mines
法取得油田所有权的方式.事实上,我甚至不确定这是不是很糟糕.如果可以,那么我想知道为什么在这种特定情况下它是可以的(虽然通常建议给参数尽可能少的"访问能力").如果它确实不好,最好改写成使用&mut self
,那么我想知道如何做到这一点,因为我自己的try 会遇到问题.
关于my attempts:
我首先要更新INSERT_MINES方法的签名
fn insert_mines(mut self, number_of_mines: u16) -> Result<Self, ()> { ... }
变成fn insert_mines(&mut self, number_of_mines: u16) -> Result<(), ()> { ... }
.
然后在new
a.f.出现以下错误:
field = field.insert_mines(number_of_mines)?;
// Type mismatch [E0308] expected `Field`, but found `()`
`?` operator has incompatible types [E0308] expected `Field`, found `()` Note: `?` operator cannot convert from `()` to `field::Field`
预期中.该方法应该就地更新该字段,因此不再需要分配任何内容.go 掉field =
的部分.
然后,insert_mines
本身出现了一个错误:
// flatten the field for an easier interaction with it
let mut flattened_field = self.0.into_iter().flatten().collect::<Vec<Cell>>();
// cannot move out of `self` which is behind a mutable reference [E0507] move occurs because `self.0` has type `std::vec::Vec<std::vec::Vec<field::cell::Cell>>`, which does not implement the `Copy` trait Note: `std::iter::IntoIterator::into_iter` takes ownership of the receiver `self`, which moves `self.0` Help: you can `clone` the value and consume it, but this might not be your desired behavior
继续解决问题,这就是它归结为:
fn insert_mines(&mut self, number_of_mines: u16) -> Result<(), ()> {
// get the width of a single row and the total cells number of the field
let (width, _, total_number_of_cells) = self.get_size();
// return an error if the required number of mines is less than 1 or is more than the total number of cells
// minus 1 (there should always be at least one cell without a mine)
if number_of_mines < 1 || number_of_mines > (total_number_of_cells - 1) {
return Err(());
}
// flatten the field for an easier interaction with it
let mut flattened_field = self.0.iter_mut().flatten().collect::<Vec<&mut Cell>>();
flattened_field.splice(
0..number_of_mines as usize,
(0..number_of_mines)
.into_iter()
.map(|_| &mut Cell::Mine)
.collect::<Vec<&mut Cell>>(),
);
// shuffle the flattened field to randomly distribute the mines
let mut rng = thread_rng();
flattened_field.shuffle(&mut rng);
// convert the flattened fields back into its original `Vec<Vec<u16>>` form
self.0 = flattened_field
.chunks(width as usize)
.map(|chunk| chunk.to_vec())
.collect::<Vec<Vec<&mut Cell>>>();
Ok(())
}
误差现在在self.0 = ...
的直线上,并表示
mismatched types [E0308] expected `Vec<Vec<Cell>>`, found `Vec<Vec<&mut Cell>>` Note: expected struct `std::vec::Vec<std::vec::Vec<field::cell::Cell>>` found struct `std::vec::Vec<std::vec::Vec<&mut field::cell::Cell>>`
Type mismatch [E0308] expected `Vec<Vec<Cell>>`, but found `Vec<Vec<&mut Cell>>`
据我所知,这意味着Field(Vec<Vec<Cell>>)
需要一个Cell
的向量,但我试图给它赋值的是一个指向Cell
的可变引用的向量.
所以,我在想,也许我采取了一种错误的方法.或者,方法可能是正确的,但我在实现中遗漏了一些东西(我的 idea 是以某种方式将<Vec<Vec<&mut Cell>>
转换回<Vec<Vec<Cell>>
,但不知道确切的how).总的来说,我在寻找关于我的问题的指导和关于我目前方法的反馈.