假设我有几个结构,如下面的示例所示,在next()
方法中,我需要使用用户提供的缓冲区拉出下一个事件,但是如果该事件是注释,并且忽略注释标志设置为true,则需要再次拉出下一个事件:
struct Parser {
ignore_comments: bool,
}
enum XmlEvent<'buf> {
Comment(&'buf str),
Other(&'buf str),
}
impl Parser {
fn next<'buf>(&mut self, buffer: &'buf mut String) -> XmlEvent<'buf> {
let result = loop {
buffer.clear();
let temp_event = self.parse_outside_tag(buffer);
match temp_event {
XmlEvent::Comment(_) if self.ignore_comments => {}
_ => break temp_event,
}
};
result
}
fn parse_outside_tag<'buf>(&mut self, _buffer: &'buf mut String) -> XmlEvent<'buf> {
unimplemented!()
}
}
但是,即使启用了
#![feature(nll)]
,此代码也会出现双重借用错误:error[E0499]: cannot borrow `*buffer` as mutable more than once at a time
--> src/main.rs:14:13
|
14 | buffer.clear();
| ^^^^^^ second mutable borrow occurs here
15 |
16 | let temp_event = self.parse_outside_tag(buffer);
| ------ first mutable borrow occurs here
|
note: borrowed value must be valid for the lifetime 'buf as defined on the method body at 12:5...
--> src/main.rs:12:5
|
12 | fn next<'buf>(&mut self, buffer: &'buf mut String) -> XmlEvent<'buf> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0499]: cannot borrow `*buffer` as mutable more than once at a time
--> src/main.rs:16:53
|
16 | let temp_event = self.parse_outside_tag(buffer);
| ^^^^^^ mutable borrow starts here in previous iteration of loop
|
note: borrowed value must be valid for the lifetime 'buf as defined on the method body at 12:5...
--> src/main.rs:12:5
|
12 | fn next<'buf>(&mut self, buffer: &'buf mut String) -> XmlEvent<'buf> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors
我可以(至少大约)理解为什么在关闭NLL功能的情况下会在这里发生错误,但是我不明白为什么在NLL中会发生错误。
无论如何,我的最终目标是在没有标志的情况下实现此功能,因此我也尝试这样做(它是递归的,这确实很不幸,但是如果没有NLL,我想出的所有非递归版本都无法使用):
fn next<'buf>(&mut self, buffer: &'buf mut String) -> XmlEvent<'buf> {
buffer.clear();
{
let temp_event = self.parse_outside_tag(buffer);
match temp_event {
XmlEvent::Comment(_) if self.ignore_comments => {}
_ => return temp_event,
}
}
self.next(buffer)
}
在这里,我试图将借用限制在一个词法块内,并且该块中的任何内容都不会泄漏到外部。但是,我仍然收到错误消息:
error[E0499]: cannot borrow `*buffer` as mutable more than once at a time
--> src/main.rs:23:19
|
15 | let temp_event = self.parse_outside_tag(buffer);
| ------ first mutable borrow occurs here
...
23 | self.next(buffer)
| ^^^^^^ second mutable borrow occurs here
24 | }
| - first borrow ends here
error: aborting due to previous error
再说一次,NLL不会修复它。
自从遇到我不了解的借阅检查错误以来已经有很长时间了,所以我希望它实际上是一些简单的事情,出于某种原因我忽略了它:)
我真的怀疑根本原因与显式的
'buf
生存期有关(特别是,打开NLL标志的错误有关于这些的注释),但是我不明白这里到底有什么问题。 最佳答案
这是a limitation of the current implementation的non-lexical lifetimes这可以用这种简化的情况来显示:
fn next<'buf>(buffer: &'buf mut String) -> &'buf str {
loop {
let event = parse(buffer);
if true {
return event;
}
}
}
fn parse<'buf>(_buffer: &'buf mut String) -> &'buf str {
unimplemented!()
}
fn main() {}
此限制可防止NLL case #3:在函数中的条件控制流
用编译器开发人员的术语来说,当前非词汇生存期的实现是“位置不敏感的”。位置敏感度最初是可用的,但是出于性能的考虑而被禁用。
I asked Niko Matsakis about this code:
好消息是,将位置敏感度的概念重新添加回去被视为对非词法生存期的实现的增强。坏消息:
(注意:它不是而不是进入了Rust 2018的初始版本)
这取决于非词汇生存期的基础实现(甚至更新!),从而提高了性能。您可以使用
-Z polonius
选择加入该半实现版本:rustc +nightly -Zpolonius --edition=2018 example.rs
RUSTFLAGS="-Zpolonius" cargo +nightly build
由于这是函数中的,因此有时可以通过内联函数来解决此问题。