mwl*_*lon 2 rust interior-mutability
假设我想要一个train可以随时在两个轨道之一之间切换并u8在其当前位置写入 a 的轨道。天真地是这样的:
struct Train<'a> {
track_a: &'a mut [u8],
track_b: &'a mut [u8],
current_track: &'a mut [u8], // either track_a or track_b
idx: usize,
}
impl<'a> Train<'a> {
pub fn new(track_a: &'a mut [u8], track_b: &'a mut [u8]) -> Self {
Self {
track_a,
track_b,
idx: 0,
current_track: track_a,
}
}
pub fn toggle_track(&mut self) {
if self.current_track == self.track_a {
self.current_track = self.track_b;
} else {
self.current_track = self.track_a;
}
}
pub fn write(&mut self, byte: u8) {
// must be fast - can't waste time choosing track here
self.current_track[self.idx] = byte;
self.idx += 1;
}
}
Run Code Online (Sandbox Code Playgroud)
重要的是,我们不能浪费时间来决定我们当前处于哪条轨道上write。当然,上面的代码无法编译,因为我们多次可变地借用track_aand 。track_b
我怎样才能让这样的东西在 Rust 中工作?我尝试使用RefCells for track_aand track_b,但意识到即使这样也没有意义,因为即使是不可变的Refs 也会提供对底层字节的可变访问。
不安全的 Rust 是实现这种数据结构的唯一方法吗?
如果您这样做,您可以在没有内部可变性、没有unsafe甚至没有单独current_track字段的情况下执行此操作:
struct Train<'a> {
track_a: &'a mut [u8], // a.k.a, the current track
track_b: &'a mut [u8],
idx: usize,
}
impl<'a> Train<'a> {
pub fn new(track_a: &'a mut [u8], track_b: &'a mut [u8]) -> Self {
Self {
track_a,
track_b,
idx: 0,
}
}
pub fn toggle_track(&mut self) {
std::mem::swap(&mut self.track_a, &mut self.track_b);
}
pub fn write(&mut self, byte: u8) {
// must be fast - can't waste time choosing track here
self.track_a[self.idx] = byte;
self.idx += 1;
}
}
Run Code Online (Sandbox Code Playgroud)
当然,这里的技巧是std::mem::swap通过交换两个轨道(其中一个我们认为是“当前”轨道)来进行切换。唯一的缺点是,如果您需要知道哪个是最初给出的“第一”和“第二”(因为这可能会在切换中丢失),但这可以通过单独的布尔值来完成,同时仍然保持write无分支。