我开始研究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的初学者,请不要犹豫,建议使用不同的数据表示。)
答案 0 :(得分:4)
正如您所注意到的,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>
。因此,编译器抱怨“摆脱借来的内容”,我们需要额外的&mut
。
答案 1 :(得分:4)
问题是什么?
您在数据中打洞:
fn swap(&mut self) -> &Board {
let tmp = self.cur; // 1
self.cur = self.next; // 2
self.next = tmp; // 3
self
}
如果我们逐行分析:
self.cur
现已未初始化self.next
现已未初始化如果出于某种原因,在第(3)行改变以收紧情况之前计算被中断,self
现在已经中毒并且可能导致各种令人讨厌的事情发生。值得注意的是,它的析构函数可能会尝试两次释放内存,例如。
理论上,您可以让编译器检查临时漏洞并毫无疑问地证明:
self
确实在某种程度上它被认为是......但事实是它很复杂,而且有很多可用的解决办法。
因此?
答案在于std::mem
,它公开了以安全的方式执行这种低级操作的函数。虽然函数本身是使用unsafe
实现的,但它们依赖于对语言和运行时的理解来暴露安全接口。
您感兴趣的两个特定功能是:
replace
:将dest: &mut T
的内容替换为src: T
并返回之前dest
swap
:交换其参数的内容使用这两个简单且安全的原语,可以避免在数据中打孔。