不能一次多次借用 `x` 作为可变的?

Cannot borrow `x` as mutable more than once at a time?

逻辑上这段代码是正确的,但是 rust 不理解上下文。尝试从具有非常短的“独占引用生命周期”的游标中读取一些字节。

为什么这段代码无法编译? Playground

struct Cursor {
    offset: usize,
    data: [u8; 4],
}
impl Cursor {
    fn read_slice(&mut self, n: usize) -> &[u8] {
        let data = &self.data[self.offset..self.offset + n];
        self.offset += n;
        data
    }
}
struct FooBar<'a> {
    foo: &'a [u8],
    bar: &'a [u8],
}

fn read_foobar_from<'a>(cursor: &'a mut Cursor) -> FooBar<'a> {
    FooBar {
        foo: cursor.read_slice(2),
        bar: cursor.read_slice(2),
    }
}

错误:

error[E0499]: cannot borrow `*cursor` as mutable more than once at a time
  --> src/main.rs:22:14
   |
19 |   fn read_foobar_from<'a>(cursor: &'a mut Cursor) -> FooBar<'a> {
   |                       -- lifetime `'a` defined here
20 | /     FooBar {
21 | |         foo: cursor.read_slice(2),
   | |              -------------------- first mutable borrow occurs here
22 | |         bar: cursor.read_slice(2),
   | |              ^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
23 | |     }
   | |_____- returning this value requires that `*cursor` is borrowed for `'a`

解决了 为什么 你会收到这个错误。 (tl;dr:Rust 不会降级借用;一个函数可变地借用一个值并返回一个具有相同生命周期的不可变引用会导致可变借用在返回的引用存在时一直存在。)

在这种情况下,我们如何解决这个问题?

当我想到“游标”时,我不会想到“拥有数据并允许读出该数据的东西”,我会想到“读出其他东西拥有的数据的东西”。事实证明,模型可以通过将返回引用的生命周期绑定到其他东西来解决这个问题。

如果我们Cursor借用数据而不是拥有它,我们可以表示返回的引用绑定到该数据的生命周期而不是可变的借 self:

struct Cursor<'a> {
    offset: usize,
    data: &'a [u8],
}
impl<'a> Cursor<'a> {
    fn read_slice(&mut self, n: usize) -> &'a [u8] {
        let data = &self.data[self.offset..self.offset + n];
        self.offset += n;
        data
    }
}

(Playground)

如果您希望 Cursor 拥有数据,那么内部可变性(例如 offset: Cell<usize>)可能是您唯一的选择:

struct Cursor {
    offset: Cell<usize>,
    data: [u8; 4],
}
impl Cursor {
    fn read_slice(&self, n: usize) -> &[u8] {
        let offset = self.offset.get();

        let data = &self.data[offset..offset + n];
        self.offset.set(offset + n);
        data
    }
}

(Playground)