我正在开发一个涉及结构体的程序,大致如下:
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]>) {
// ...
}
}
我已经实现了一个迭代器,将
data
和overlay
中的信息合并后,供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
函数,但总是针对相同的数据/覆盖。因此,我考虑添加一个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.sink 的可变借用,而此时 self 已经被借用了。
我已经与这个问题纠缠了很长时间,如果我理解正确,问题不在于 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)
'a
可以成为调用者选择的任意生命周期,因此它可能会选择'static
。但你的代码并没有返回一个具有静态生命周期的引用,它返回的是一个具有self.data
生命期的引用。这显然行不通,所以这样的签名被认为是不安全的。顺便说一句,编译器通常会显示冲突要求 - 它会突出显示相关生命周期的作用域。你的其余问题需要进行更多的研究。 - Vladimir Matveev&Vec<T>
可以通过将其更改为&[T]
来变得更加高效;&Vec<T>
也会自动转换为&[T]
,因此在使用它时不会增加负担。 - Chris Morgan