I have several parsers. There is a top-level one that can delegate to another one.
Parser
s get their input from a Reader
(mutable). I want only one Parser
to be able to parse at a time, by only one parser should have the Reader
.
I did this by making an enum for the top level parser that is either the reader, or the delegated parser (which has a reader). That way it can only read when not delegated, which is what I want.
From the top level parser, I need to mutably borrow this enum to determine what to do and to get the reader or delegate parser. The problem is that if I want to start or stop delegating, I need to move the Reader
around. But it's still mutably borrowed at this point.
I have created a minimal example, and I've included the suggestions from the comments regarding replace
and non-lexical lifetimes:
#![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
}
}
With the comment suggestions, one error remains:
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`
I believe I can probably solve this by taking reader
out of ReaderOrDelegate
and putting it in each parser as an Rc<RefCell<Parser>>>
. But I think having it in the enum is more logical: there should be only one parser at a time able to use the reader. Is this possible?
I get that the errors make sense in this case, but I feel that on a high level, it should be possible to do what I want. There only ever needs to be one owner for Reader
.
EDIT: To me it seems non-trivial how the question with replace
can be applied to this case with 'nesting' (reader
already borrowed by match
and then want to swap a field). So while possibly related, I don't this the other question is enough to solve this one. Not for me anyway.
EDIT 2: Included the comment suggestions in the code sample and error.