如何交换结构的两个字段

How to swap two fields of a struct

我已经开始学习 Rust,并且正在尝试实现一个简单的一维元胞自动机。我想将自动机状态 (Board) 表示为包含大小和两个不同向量(相同大小)的结构。我试过了:

struct Board {
    n: usize,
    cur:  Vec<u32>,
    next: Vec<u32>,
}

impl Board {
    fn new(size: usize) -> Board {
        Board {
            n: size,
            cur: vec![0;size],
            next: vec![0;size],
        }
    }
}

到目前为止一切顺利。我也能够改变这两个向量。但是后来我希望能够交换两个向量(或者更确切地说是它们的引用),例如:

fn swap(&mut self) -> &Board {
    let tmp = self.cur;
    self.cur = self.next;
    self.next = tmp;
    self
}

它失败了,cannot move out of borrowed content [E0507] 我想我能理解。我还尝试了 mem::swap,我在类似标题的问题中找到了它,但没有成功。

我怎样才能让这个例子起作用? (由于我是 Rust 的初学者,请不要犹豫建议不同的数据表示)。

如您所见,mem::swap 是要走的路:

fn swap(&mut self) -> &Board {
    std::mem::swap(&mut self.cur, &mut self.next);
    self
}

这行得通。请注意,当使用 . 时,您正在取消引用 self。因此,虽然 self 的类型为 &mut Board,但 self.cur 的类型为 Vec<u32>。因此编译器抱怨 "move out of borrowed content" 而我们需要额外的 &muts.

What's the problem?

您在数据中打孔

fn swap(&mut self) -> &Board {
    let tmp = self.cur;         // 1
    self.cur = self.next;       // 2
    self.next = tmp;            // 3
    self
}

如果我们逐行分析:

  1. self.cur 现在未初始化
  2. self.next 现在未初始化
  3. 一切都再次符合犹太洁食标准

如果在第 (3) 行发生变化以收紧情况之前由于某种原因计算被中断,self 现在中毒并可能导致各种讨厌的事情发生。值得注意的是,它的析构函数可能会尝试释放内存两次。

理论上,您可以让编译器检查临时漏洞并毫无疑问地证明:

  • 打孔时没有函数访问 self
  • 到了作用域的尽头,不管是正常到达还是展开,空洞又被填满了

确实在某些时候它被考虑过......但事实是它很复杂并且有现成的解决方法。

So?

答案在于 std::mem,它公开了以安全方式执行此类低级操作的功能。虽然函数本身是使用 unsafe 在幕后实现的,但它们依靠对语言和运行时的理解来公开安全接口。

您会感兴趣的两个特定函数是:

  • replace:将dest: &mut T的内容替换为src: T和returns之前包含在dest
  • 后面的内容
  • swap: 交换其参数的内容

使用这两个简单而安全的原语,您可以避免在数据中打孔。