我正试着用铁 rust 来写俄罗斯方块.我在这个项目中有一些 struct ,我想将其视为immutable个,即使它们发生了do个Mutations .

我用来实现这种行为的方法是:

#[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 mut matrix = Matrix::new();
    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
}

Playground

这提供了:

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::交换、Std::Mem::Take或STD::Mem::Replace、

可以为我做这个戏法,但我不确定是怎么做的.

推荐答案

你是对的.mem::[take,replace]()个人可以做这项工作.

问题是while you can leave a 100 uninitialized for a time, you cannot leave a 101 uninitialized for a time (by moving out of it), 102.

这种限制是有原因的:panic .如果matrix.solidify()死机,我们将退出而不执行对matrix的递归赋值.后来,我们可以从panic 中恢复过来,观察从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箱.它提供了replace_with::replace_with_or_abort(),这将在出现panic 的情况下中止整个过程,防止恢复的可能性:

impl Tetris {
    fn change(&mut self) {
        replace_with::replace_with_or_abort(&mut self.matrix, |matrix| matrix.solidify(0, 0));
    }
}

请注意,你可能想要interior mutability英镑,而不是你现在正在做的.

Rust相关问答推荐

阻止websocket中断的中断中断的终端(操作系统错误4)

borrow 和内部IntoIterator

为什么类型需要在这个代码中手动指定,在rust?

如何在Rust中表示仅具有特定大小的数组

为什么BitVec缺少Serialize trait?

像这样的铁 rust 图案除了‘选项’之外,还有其他 Select 吗?

如何计算迭代器适配器链中过滤的元素的数量

如何为 struct 字段设置新值并在Ruust中的可变方法中返回旧值

铁 rust 中的泛型:不能将`<;T作为添加>;::Output`除以`{Float}`

作为1字节位掩码的布尔值 struct

为什么 `Deref` 没有在 `Cell` 上实现?

处理带有panic 的 Err 时,匹配臂具有不兼容的类型

当我编译 Rust 代码时,我是否缺少 AVX512 的目标功能?

RUST 中的读写器锁定模式

Rust 并行获取对 ndarray 的每个元素的可变引用

为什么 Rust 字符串没有短字符串优化 (SSO)?

为什么 no_std crate 可以依赖于使用 std 的 crate?

从 Cranelift 发出 ASM

提取 struct 生成宏中字段出现的索引

覆盖类型的要求到底是什么?为什么单个元素元组满足它?