我正在尝试用 Rust 编写俄罗斯方块。我在这个项目中有一些结构,我想将其视为 immutable,即使它们 do 变异。
我用来实现这种行为的方法是这样的:
#[derive(Debug)]
struct Example {
foo: i8
}
impl Example {
fn change(mut self) -> Self {
self.foo = 8;
self
}
}
允许你做这样的事情:
let first = Example { foo: 0 };
let second = first.change();
println!("{:?}", second); // Example { foo: 8 }
但是当你做这样的事情时会对你大喊大叫:
let first = Example { foo: 0 };
let second = first.change();
println!("{:?}", first); // error[E0382]: borrow of moved value: `first`
我感到困惑的部分是,为什么这样做有效:
#[derive(Debug)]
struct Matrix {
cells: [[char; 2]; 2]
}
impl Matrix {
fn new() -> Self {
Matrix {
cells: [['░'; 2]; 2]
}
}
fn solidify(mut self, row: usize, column: usize) -> Self {
self.cells[row][column] = '█';
self
}
}
fn main() {
let matrix = Matrix::new();
let matrix = matrix.solidify(0, 0);
println!("{:?}", matrix); // Matrix { cells: [['█', '░'], ['░', '░']] }
}
什么时候没有?
#[derive(Debug)]
struct Matrix {
cells: [[char; 2]; 2]
}
impl Matrix {
fn new() -> Self {
Matrix {
cells: [['░'; 2]; 2]
}
}
fn solidify(mut self, row: usize, column: usize) -> Self {
self.cells[row][column] = '█';
self
}
}
#[derive(Debug)]
struct Tetris {
matrix: Matrix
}
impl Tetris {
fn new() -> Self {
Tetris {
matrix: Matrix::new()
}
}
fn change(&mut self) {
self.matrix = self.matrix.solidify(0, 0);
/* -----------------------------------------
This is where it yells at me ^ */
}
}
fn main() {
let mut tetris = Tetris::new();
tetris.change();
println!("{:?}", tetris); // error[E0507]: cannot move out of `self.matrix` which is behind a mutable reference
}
这给出:
error[E0507]: cannot move out of `self.matrix` which is behind a mutable reference
--> src/main.rs:32:23
|
32 | self.matrix = self.matrix.solidify(0, 0);
| ^^^^^^^^^^^ -------------- `self.matrix` moved due to this method call
| |
| move occurs because `self.matrix` has type `Matrix`, which does not implement the `Copy` trait
|
note: `Matrix::solidify` takes ownership of the receiver `self`, which moves `self.matrix`
--> src/main.rs:13:21
|
13 | fn solidify(mut self, row: usize, column: usize) -> Self {
| ^^^^
我做了一些研究,我觉得要么 std::mem::swap,std::mem::take,或 std::mem::replace,
可以帮我解决这个问题,但我不确定怎么做。
你是对的。
mem::[take,replace]()
可以完成工作。
问题是虽然你可以让一个变量暂时未初始化,但你不能让一个可变引用暂时没有初始化(通过移出它),即使你在之后重新分配它。
这种限制是有原因的:恐慌。如果
matrix.solidify()
恐慌,我们将退出而不执行对 matrix
的重复分配。稍后,我们可以从恐慌中恢复过来,观察移动的matrix
.
没有依赖项(和不安全的代码),唯一的解决方案是即使我们重新分配也留下一些东西,这样即使我们恐慌
matrix
保持初始化。 std::mem::take()
可以帮助解决这个问题,如果 Matrix
实现 Default
- 它保留默认值,而更一般的 std::mem::replace()
可以帮助否则 - 它留下 some 值:
#[derive(Debug, Default)]
struct Matrix {
cells: [[char; 2]; 2]
}
impl Tetris {
fn change(&mut self) {
let matrix = std::mem::take(&mut self.matrix);
self.matrix = matrix.solidify(0, 0);
}
}
或:
#[derive(Debug)] // No `Default`.
struct Matrix {
cells: [[char; 2]; 2]
}
impl Tetris {
fn change(&mut self) {
let matrix = std::mem::replace(&mut self.matrix, Matrix::new());
self.matrix = matrix.solidify(0, 0);
}
}
如果这对你来说不够好(例如,因为你没有一个好的默认值可以插入,或者因为性能要求)你可以使用
replace_with
crate。它提供了replace_with::replace_with_or_abort()
,在出现恐慌的情况下只会中止整个过程,从而防止恢复的可能性:
impl Tetris {
fn change(&mut self) {
replace_with::replace_with_or_abort(&mut self.matrix, |matrix| matrix.solidify(0, 0));
}
}
请注意,您可能实际上想要内部可变性,而不是您现在正在做的事情。
我最近发现我真的不需要 replace_with 箱子 如果我将相同的
own mutable self and then return mutated self
模式应用于我的 change
函数。
我一直要做的就是改变这个:
impl Tetris {
/* ... */
fn change(&mut self) {
self.matrix = self.matrix.solidify(0, 0);
/* -----------------------------------------
This is where it yells at me ^ */
}
/* ... */
}
对此:
impl Tetris {
/* ... */
fn change(mut self) -> Self {
self.matrix = self.matrix.solidify(0, 0);
/* -----------------------------------------
It doesn't yell at me here anymore ^ :D */
self
}
/* ... */
}
然后像这样使用新代码:
fn main() {
let tetris = Tetris::new();
let tetris = tetris.change();
println!("{:?}", tetris); // Tetris { matrix: Matrix { cells: [['█', '░'], ['░', '░']] } }
}