为可以委托的解析器借用检查器问题

Borrow checker problems for parser that can delegate

我有几个解析器。有一个顶层可以委托给另一个。

Parsers 从 Reader(可变)获取输入。我只希望一个 Parser 能够一次解析,只有一个解析器应该具有 Reader.

我通过为顶级解析器创建一个枚举来做到这一点,该解析器可以是 reader,也可以是委托解析器(具有 reader)。这样它只能在未委托时读取,这就是我想要的。

从顶级解析器,我需要可变地借用这个枚举来确定要做什么并获得 reader 或委托解析器。问题是,如果我想开始或停止委派,我需要移动 Reader。但此时它仍然是可变借用的。

我已经创建了一个最小的例子,并且我已经包含了关于 replace 和非词汇生命周期的评论的建议:

#![feature(nll)]
use std::mem::replace;

struct Reader {
    i: u8,
}
impl Reader {
    fn next(&mut self) -> u8 {
        /* some logic here */
        self.i += 1;
        self.i
    }
}

trait Parser {
    fn parse(&mut self) -> u8;
}

enum ReaderOrDelegate {
    Read(Reader),
    Delegate(AnotherParser),  /* Trait object in reality, but keeping it simple here. */
}

struct OneParser {
    reader_or_delegate: ReaderOrDelegate,
}
impl Parser for OneParser {
    fn parse(&mut self) -> u8 {
        match self.reader_or_delegate {
            ReaderOrDelegate::Delegate(ref mut delegate) => {
                match delegate.parse() {
                    0 => {
                        replace(&mut self.reader_or_delegate, ReaderOrDelegate::Read(delegate.consume()));
                        self.parse()
                    },
                    x => 2 * x
                }
            },
            ReaderOrDelegate::Read(ref mut reader) => {
                match reader.next() {
                    0 => {
                        replace(&mut self.reader_or_delegate, ReaderOrDelegate::Delegate(AnotherParser { reader }));
                        self.parse()
                    },
                    x => 3 * x
                }
            },
        }
    }
}

struct AnotherParser {
    reader: Reader,
}
impl AnotherParser {
    fn consume(self) -> Reader {
        self.reader
    }
}
impl Parser for AnotherParser {
    fn parse(&mut self) -> u8 {
        self.reader.next() * 2
    }
}

有了评论建议,还有一个错误:

error[E0308]: mismatched types
  --> src/main.rs:42:106
   |
42 |                         replace(&mut self.reader_or_delegate, ReaderOrDelegate::Delegate(AnotherParser { reader }));
   |                                                                                                          ^^^^^^ expected struct `Reader`, found &mut Reader
   |
   = note: expected type `Reader`
              found type `&mut Reader`

我相信我可以通过从 ReaderOrDelegate 中取出 reader 并将其作为 Rc<RefCell<Parser>>> 放入每个解析器来解决这个问题。但我认为将它放在枚举中更合乎逻辑:一次应该只有一个解析器能够使用 reader。这可能吗?

我知道在这种情况下错误是有道理的,但我觉得在高层次上,应该可以做我想做的事。 Reader.

只需要一个所有者

编辑:对我来说,replace 的问题如何应用到 'nesting' 的情况下似乎很重要(reader 已经被 match 借用了,然后想交换一个字段)。因此,虽然可能相关,但我认为另一个问题不足以解决这个问题。反正不适合我。

编辑 2:在代码示例和错误中包含注释建议。

考虑以下行:

replace(&mut self.reader_or_delegate, ReaderOrDelegate::Delegate(AnotherParser { reader }));

你需要一个 reader 值,而不是一个引用,来构建 anotherParser:

error[E0308]: mismatched types
  --> src/main.rs:42:106
   |
42 |                         replace(&mut self.reader_or_delegate, ReaderOrDelegate::Delegate(AnotherParser { reader }));
   |                                                                                                          ^^^^^^ expected struct `Reader`, found &mut Reader
   |
   = note: expected type `Reader`
              found type `&mut Reader`

但是不可能获得这样的值。如果我们尝试:

ReaderOrDelegate::Read(reader) => {
    match reader.next() {
        0 => {
            replace(&mut self.reader_or_delegate, ReaderOrDelegate::Delegate(AnotherParser { reader }));
            self.parse()
        },
        x => 3 * x
    }
},

我们现在找到了您设计的真正问题:

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:39:36
   |
39 |             ReaderOrDelegate::Read(reader) => {
   |                                    ^^^^^^ cannot move out of borrowed content

parse方法借用了self,这意味着此时我们不能从中移出拥有的值 借来的 self.

另请注意,错误 E0507 也适用于以下行:

replace(&mut self.reader_or_delegate, ReaderOrDelegate::Read(delegate.consume()));

因为consume试图从借来的delegate中移出一个值。

在你的代码中,编译器没有显示这个问题,但如果你注释掉显然是你的示例中唯一存在的问题的行,它就会出现。

我能够安排的唯一解决方案,不会引起借用检查器错误,也不会 转换太多 您的设计基于使用引用计数器 Reader,在顶级解析器和委托解析器之间共享。

使用 Rc<Reader> 您只有一个 reader 通过智能指针与您的解析器共享。

ReadOrDelegate enum 仅指示活动解析器,不再拥有 reader 移动。

#![feature(nll)]
use std::rc::Rc;

struct Reader {
    i: u8,
}

impl Reader {
    fn next(&mut self) -> u8 {
        /* some logic here */
        self.i += 1;
        self.i
    }
}

trait Parser {
    fn parse(&mut self) -> u8;
}

enum ReaderOrDelegate {
    Read,
    Delegate,
}

struct OneParser {
    reader_or_delegate: ReaderOrDelegate,
    reader: Rc<Reader>,
    delegate: AnotherParser
}

impl Parser for OneParser {
    fn parse(&mut self) -> u8 {
        match self.reader_or_delegate {
            ReaderOrDelegate::Delegate => {
                match self.delegate.parse() {
                    0 => {
                        self.reader_or_delegate = ReaderOrDelegate::Read;
                        self.parse()
                    },
                    x => 2 * x
                }
            },
            ReaderOrDelegate::Read => {
                match Rc::get_mut(&mut self.reader).unwrap().next() {
                    0 => {
                        self.reader_or_delegate = ReaderOrDelegate::Delegate;
                        self.parse()
                    },
                    x => 3 * x
                }
            },
        }
    }
}

struct AnotherParser {
    reader: Rc<Reader>
}

impl Parser for AnotherParser {
    fn parse(&mut self) -> u8 {
        Rc::get_mut(&mut self.reader).unwrap().next() * 2
    }
}