【问题标题】:How to deal with multiple mutable borrows in Rust with match?如何使用匹配处理 Rust 中的多个可变借用?
【发布时间】:2018-05-15 06:04:28
【问题描述】:

我正在尝试使用 quick-xml 封装一个 XML 解析器,该解析器需要一些特定的 XML 条目。主要代码如下:

pub struct GPXParser<B: BufRead> {
    reader: Reader<B>,
    buff: Vec<u8>,
}

impl<B> GPXParser<B> {
    pub fn read_next<a>(&mut self) -> Result<XMLCache, Error> {
        match self.reader.read_event(&mut self.buff) {
            Ok(XMLEvent::Start(ref e)) if e.name() == b"wpt" => {
                self.read_next_wpt() // --> Multiple mutable borrows error
            }
            _ => Err(Error::NoCaches),
        }
    }

    fn read_next_wpt(&mut self) -> Result<XMLCache, Error> {
        match self.reader.read_event(&mut self.buff) {
            _ => Err(Error::NoCaches),
        }
    }
}

然后我在 rust-lang.org 上阅读了这个 topic,提到:

惯用的 Rust 代码通常避免持有对可变对象内部的长期引用,并倾向于使用不可变引用或独立值的替代方法

我尝试使用中间 buff 元素来改变我的方法:

pub struct GPXParser<B: BufRead> {
    reader: Reader<B>,
}

impl<B> GPXParser<B> {
    pub fn read_next<a>(&mut self) -> Result<XMLCache, Error> {
        let mut buff: Vec<u8> = Vec::new();

        match self.reader.read_event(&mut buff) {
            Ok(XMLEvent::Start(ref e)) if e.name() == b"wpt" => {
                self.read_next_wpt(&mut buff) // --> Multiple mutable borrows error
            }
            _ => Err(Error::NoCaches),
        }
    }

    fn read_next_wpt(&mut self, buff: &mut Vec<u8>) -> Result<XMLCache, Error> {
        match self.reader.read_event(buff) {
            _ => Err(Error::NoCaches),
        }
    }
}

我没有改变方法。

我试图将match 语句分成两行:

pub fn read_next<a>(&mut self) -> Result<XMLCache, Error> {
    let result = self.reader.read_event(&mut self.buff);
    match result {
        Ok(XMLEvent::Start(ref e)) if e.name() == b"wpt" => self.read_next_wpt(),
        _ => Err(Error::NoCaches),
    }
}

但我得到了同样的错误:

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/gpx/mod.rs:34:17
   |
31 |         let result = self.reader.read_event(&mut self.buff);
   |                                                  --------- first mutable borrow occurs here
...
34 |                 self.read_next_wpt()
   |                 ^^^^ second mutable borrow occurs here
...
39 |     }
   |     - first borrow ends here

有没有办法在不同的方法调用中使用相同的缓冲区?

应该在游戏中加入一些生命吗?

如果不是,解决这个问题的惯用 Rust 方法是什么?

【问题讨论】:

  • 你为什么使用XMLEvent::Start(ref e)?为什么不XMLEvent::Start(e)
  • 我只是看了一下存储库 github.com/tafia/quick-xml 上的自述文件。这有什么含义吗?两者有什么区别?
  • 如果你删除ref,它会编译吗?如果有,我会写一个答案
  • 否 :( 我还有另一个错误cannot bind by-move into a pattern guard,它指向e 符号,其解释是`将值移动到模式保护中`

标签: rust borrow-checker


【解决方案1】:

问题来自prototype for read_event中的'b生命周期:

pub fn read_event<'a, 'b>(
    &'a mut self, 
    buf: &'b mut Vec<u8>
) -> Result<Event<'b>>

这将缓冲区的生命周期与返回值的生命周期联系起来,因此只要结果还存在,就不能重复使用缓冲区。当你不想递归时,你可以通过提前返回来绕过它,并且只有在你使用完结果后才递归:

pub fn read_next<a>(&mut self) -> Result<XMLCache, Error> {
    match self.reader.read_event(&mut self.buff) {
        Ok(XMLEvent::Start(ref e)) if e.name() == b"wpt" => (),
        _ => { return Err(Error::NoCaches) },
    }
    self.read_next_wpt()
}

如果您想添加更多案例,您必须首先从结果中提取您希望使用的任何相关信息,包括选择要调用的方法所需的任何信息,然后在调用方法之前让结果超出范围.例如:

pub fn read_next<a>(&mut self) -> Result<XMLCache, Error> {
    let next_method = match self.reader.read_event(&mut self.buff) {
        Ok(XMLEvent::Start(ref e)) if e.name() == b"wpt" => GPXParser<B>::read_next_wpt,
        Ok(XMLEvent::Start(ref e)) if e.name() == b"foo" => GPXParser<B>::read_next_foo,
        _ => { return Err(Error::NoCaches) },
    }
    next_method(self)
}

或者,如果性能影响小到可以接受(您应该对其进行测量以支持您的决定),则每次只使用不同的缓冲区可能会更容易。


以下原始答案供参考:

尝试将read_event 和匹配项拆分为单独的行:

let result = self.reader.read_event(&mut self.buff);
match result {
    ...
}

您的问题是整个匹配表达式的缓冲区是可变借用的,因此您不能在匹配臂内重新借用缓冲区。通过将代码分成两行,缓冲区只为第一个表达式(let result=...)借用,并且可以在匹配中再次借用。

这可能会在未来非词法生命周期变得稳定时得到解决。

【讨论】:

  • 我总是通过将语句分成两行来得到同样的错误:(
  • 那你能给出完整的错误信息吗?特别是编译器应该说明可变借用发生的确切位置。
  • 在调用第二种方法self.read_next_wpt()
  • 请给完整的留言好吗?
  • 新答案很好,但如果我添加更多案例调用其他方法怎么办?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-12
  • 2018-04-15
  • 1970-01-01
相关资源
最近更新 更多