假设我有以下示例中的几个 struct ,在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!()
    }
}

然而,这段代码给出了一个双borrow 错误,即使我启用了#![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中会发生错误.

无论如何,我的最终目标是在没有标志的情况下实现它,所以我也try 过这样做(它是递归的,这真的很不幸,但我提出的所有非递归版本都不可能在没有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)
}

在这里,我试图将借词限制在一个词法块内,而这个词法块中的nothing个词会泄漏到外部.然而,我仍然得到一个错误:

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标志的错误有这些注释),但我不明白这里到底出了什么问题.

推荐答案

这是non-lexical lifetimes个 case 中的a limitation of the current implementation个,可以通过这个简化 case 来展示:

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:conditional control flow across functions

用编译器开发人员的话说,非词汇生存期的当前实现是"位置不敏感"的.位置敏感度最初是可用的,但以性能的名义被禁用.

I asked Niko Matsakis about this code:

在您的示例中:值event只需要有条件地具有生存期'buf——在可能执行或可能不执行的返回点.但当我们"位置不敏感"时,我们只跟踪event人在任何地方必须拥有的生命,而不考虑生命必须在哪里.在本例中,这意味着我们让它在任何地方都保持不变,这就是编译失败的原因.

一件微妙的事情是,当前的分析在一个方面是位置敏感的——借款发生在哪里.借款期限不长.

好消息是,重新加入位置敏感度的概念被视为对非词汇生命周期实现的增强.坏消息是:

这可能在[Rust 2018]版本之前,也可能不会.

(注:2018年Rust首次发布时,它确实发布了not条)

这取决于一个(甚至更新的!)提高性能的非词汇生存期的底层实现.您可以 Select 使用-Z polonius:

rustc +nightly -Zpolonius --edition=2018 example.rs
RUSTFLAGS="-Zpolonius" cargo +nightly build

因为这是across functions,所以有时可以通过内联函数来解决这个问题.

Rust相关问答推荐

如何初始化match声明中的两个变量而不会激怒borrow 判断器?

当为a Self:IntoIterator设置trait bind `时,获取`a T `不是迭代器"&'"<'>&'

关于Rust 中回归的逻辑

当两者都有效时,为什么Rust编译器建议添加';&;而不是';*';?

为什么铁 rust S的默认排序功能比我对小数组的 Select 排序稍微慢一些?

`*mut[T]`与`*mut T`的区别

如何在函数中返回自定义字符串引用?

AXUM一路由多个不包括URL的参数类型

无法将 rust 蚀向量附加到另一个向量

在Rust中克隆源自INTO_ITER()的迭代器的成本?

如何在Rust中缩短数组

UnsafeCell:它如何通知 rustc Select 退出基于别名的优化?

Rust 重写函数参数

std mpsc 发送者通道在闭包中使用时关闭

在描述棋盘时如何最好地使用特征与枚举

有没有办法隐式绑定 let/match 操作的成员?

Rust:`sort_by` 多个条件,冗长的模式匹配

以下打印数组每个元素的 Rust 代码有什么问题?

Rust 中的运行时插件

如何在不设置精度的情况下打印浮点数时保持尾随零?