你能控制借用结构还是借用字段?

Can you control borrowing a struct vs borrowing a field?

我正在开发一个涉及以下结构的程序:

struct App {
    data: Vec<u8>,
    overlay: Vec<(usize, Vec<u8>)>,
    sink: Sink,
}

简而言之,data 字段包含一些字节,overlay 是要插入到特定索引处的一系列字节序列。 Sink 类型并不重要,只是它具有如下函数:

impl Sink {
    fn process<'a>(&mut self, input: Vec<&'a [u8]>) {
        // ...
    }
}

我已经实现了一个迭代器来合并来自 dataoverlay 的信息以供 Sink 使用。

struct MergeIter<'a, 'b> {
    data: &'a Vec<u8>,
    overlay: &'b Vec<(usize, Vec<u8>)>,
    // iterator state etc.
}

impl<'a, 'b> Iterator for MergeIter<'a, 'b> {
    type Item = &'a [u8];
    // ...
}

我认为这是一个小小的谎言,因为迭代器返回的每个 &[u8] 的生命周期并不总是原始 data 的生命周期。从 overlay 插入的数据具有不同的生命周期,但我不知道如何更准确地对此进行注释。无论如何,借用检查员似乎并不介意 - 以下方法有效:

fn merge<'a, 'b>(data: &'a Vec<u8>, overlay: &'b Vec<(usize, Vec<u8>)>, start: usize) -> Vec<&'a [u8]> {
    MergeIter::new(data, overlay, start).collect()
}

impl App {
    fn process(&mut self) {
        let merged = merge(&self.data, &self.overlay, 0);
        // inspect contents of 'merged'
        self.sink.process(merged);
    }
}

我最终到处都在使用这个 merge 函数,但总是针对同一个 data/overlay。所以我想为了方便起见我会添加一个 App::merge 函数,这就是问题开始的地方:

impl App {
    fn merge<'a>(&'a self, start: usize) -> Vec<&'a [u8]> {
        MergeIter::new(&self.data, &self.overlay, start).collect()
    }

    fn process(&mut self) {
        let merged = self.merge(0);
        // inspect contents of 'merged'
        self.sink.process(merged);
    }
}

App::process 现在无法通过借用检查器 - 它拒绝在借用 self 时允许 self.sink 的可变借用。

我已经为此苦苦挣扎了一段时间,如果我理解正确的话,问题不在于 process 而在于这个签名:

fn merge<'a>(&'a self, start: usize) -> Vec<&'a [u8]> {

在这里我基本上告诉借用检查器向量中返回的引用等同于 self 借用。

尽管我觉得我现在已经理解了问题,但我仍然觉得我的手被束缚了。留下生命周期注释没有帮助(因为编译器做了等效的?),并且只涉及两个引用,我无法告诉 Rust 输出引用具有绑定到其他东西的生命周期。

我也试过这个:

fn merge<'a, 'b>(&'b self, start: usize) -> Vec<&'a [u8]> {
    let data: &'a Vec<u8> = &self.data;
    MergeIter::new(&self.data, &self.overlay, start).collect()
}

但是编译器抱怨 let 语句(“由于需求冲突,无法推断出适当的生命周期”——我还发现编译器没有解释上述需求令人气愤)。

有可能实现吗? Rust 参考对生命周期注释和相关语法有一定的启发。

rustc 1.0.0-nightly (706be5ba1 2015-02-05 23:14:28 +0000)

只要方法 merge 采用 &self,您就无法完成您想要的:它借用了它的所有参数并且无法更改。

解决方案是更改它,使其不占用 self,而是占用您希望借用的各个字段:

impl App {
    ...
    fn merge(data: &Vec<u8>, overlay: &Vec<(usize, Vec<u8>)>, start: usize) -> Vec<&[u8]> {
        MergeIter::new(data, overlay, start).collect()
    }

    fn process(&mut self) {
        let merged = Self::merge(&self.data, &self.overlay, 0);
        ... // inspect contents of 'merged'
        self.sink.process(merged);
    }
}

是的,您猜对了 - 发生错误是因为当您使用 merge 方法接受 &self 时,编译器无法在其调用站点知道它仅使用了某些字段 - merge 签名只告诉它它 returns 的数据以某种方式从 self 派生,但它没有说明如何 - 因此编译器假设“最坏”的情况并阻止你访问其他字段 self 有。

恐怕目前没有办法解决这个问题,而且我不确定将来会有什么办法。但是,您可以使用宏来缩短 merge 调用:

macro_rules! merge {
    ($this:ident, $start:expr) => {
        MergeIter::new(&$this.data, &$this.overlay, $start).collect()
    }
}

fn process(&mut self) {
    let merged = merge!(self, 0);
    // inspect contents of 'merged'
    self.sink.process(merged);
}