你能控制借用一个结构而不是借用一个字段吗?

时间:2015-04-27 13:04:48

标签: rust borrow-checker

我正在研究一个涉及这些方面的结构的程序:

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];
    ...
}

这是一个轻微的谎言,因为迭代器返回的每个&amp; [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功能,但始终针对相同的数据/叠加层。所以我想我会添加一个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借用。

即使我觉得我现在已经理解了这个问题,但我仍觉得我的双手被束缚了。抛弃生命周期注释没有用(因为编译器做了相同的操作?),并且只涉及两个引用,我无法告诉生锈,输出引用的生命周期与其他内容绑定。

我也试过这个:

    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 Reference对生命周期注释和相关语法有所了解......

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

2 个答案:

答案 0 :(得分:5)

只要方法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);
    }
}

答案 1 :(得分:2)

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

我担心目前无法解决这个问题,我不确定会不会有任何问题。但是,您可以使用宏来缩短self调用:

self