Rust 如何以可变方式引用多个内容之一
问题描述
假设我想要一个可以随时在两个轨道中切换的train
,并在其当前位置写入一个u8
。最直接的办法可能是这样的:
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;
}
}
重要的是,我们不能浪费时间来决定当前在哪个轨道上的问题。
write
。当然,上面的代码不能编译,因为我们对 track_a
and track_b
进行了多次可变借用。
在Rust中如何实现类似的功能呢?我尝试使用 RefCell
对 track_a
和 track_b
进行操作,但是意识到即使是不可变 Ref
也会给予对底层字节的可变访问。
难道使用unsafe 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;
}
}
这里的关键当然是使用std::mem::swap
来通过交换两个轨道(其中一个被认为是“当前”轨道)来进行切换。唯一的缺点是,如果你需要知道最初的“第一个”和“第二个”轨道,那可能会在切换中丢失(因为这可能被认为是第一个轨道)。但是,可以通过一个单独的布尔值来完成这一点,同时保持无分支的write
。