Dnd*_*ndx 3 rust borrow-checker
如何在不进行数组复制或b()每次迭代多次调用的情况下使此示例进行编译- b()必须执行一些昂贵的解析?
这不是我编写的完整代码,但是它说明了我遇到的问题。在这里,Test正在尝试执行某种流解析工作。c()是解析函数,Some解析成功后返回。b()是一项功能,当c()无法使用可用数据进行解析时,它将尝试从流中读取更多数据。返回的值是self.v包含已解析范围的切片。
struct Test {
v: [u8; 10],
index: u8,
}
impl Test {
fn b(&mut self) {
self.index = 1
}
fn c(i: &[u8]) -> Option<&[u8]> {
Some(i)
}
fn a(&mut self) -> &[u8] {
loop {
self.b();
match Test::c(&self.v) {
Some(r) => return r,
_ => continue,
}
}
}
}
fn main() {
let mut q = Test {
v: [0; 10],
index: 0,
};
q.a();
}
Run Code Online (Sandbox Code Playgroud)
编译时,将产生以下借用检查器错误:
struct Test {
v: [u8; 10],
index: u8,
}
impl Test {
fn b(&mut self) {
self.index = 1
}
fn c(i: &[u8]) -> Option<&[u8]> {
Some(i)
}
fn a(&mut self) -> &[u8] {
loop {
self.b();
match Test::c(&self.v) {
Some(r) => return r,
_ => continue,
}
}
}
}
fn main() {
let mut q = Test {
v: [0; 10],
index: 0,
};
q.a();
}
Run Code Online (Sandbox Code Playgroud)
如果我更改a()为:
fn a(&mut self) -> Option<&[u8]> {
loop {
self.b();
if let None = Test::c(&self.v) {
continue
}
if let Some(r) = Test::c(&self.v) {
return Some(r);
} else {
unreachable!();
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后运行,但有明显的缺点,即调用c()两次解析函数。
我有点理解,self当返回值取决于它的时候进行更改是不安全的,但是self.v,当我们尝试b()再次调用时,为什么不可变的借项在下一次迭代中仍然存在,我有点理解。
目前,“ Rustc不能通过有条件的借款回报”进行交易”。请参阅Gankro关于此问题的评论21906。
如果只有一个执行路径终止循环,则无法为借项分配正确的生存期。
我可以建议这种解决方法,但我不确定它是否最佳:
fn c(i: &[u8]) -> Option<(usize, usize)> {
Some((0, i.len()))
}
fn a(&mut self) -> &[u8] {
let parse_result;
loop {
self.b();
match Test::c(&self.v) {
Some(r) => {
parse_result = r;
break;
}
_ => {}
}
}
let (start, end) = parse_result;
&self.v[start..end]
}
Run Code Online (Sandbox Code Playgroud)
您可以使用数组索引构造解析结果,并将其转换为循环外的引用。
另一种选择是诉诸unsafe于解耦寿命。我不是安全使用不安全的专家,因此请注意他人的评论。
fn a(&mut self) -> &[u8] {
loop {
self.b();
match Test::c(&self.v) {
Some(r) => return unsafe{
// should be safe. It decouples lifetime of
// &self.v and lifetime of returned value,
// while lifetime of returned value still
// cannot outlive self
::std::slice::from_raw_parts(r.as_ptr(), r.len())
},
_ => continue,
}
}
}
Run Code Online (Sandbox Code Playgroud)