如何在 Rust 中一次可变地引用多个事物之一?

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 是实现这种数据结构的唯一方法吗?

kmd*_*eko 5

如果您这样做,您可以在没有内部可变性、没有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无分支。