0

I have several parsers. There is a top-level one that can delegate to another one.

Parsers 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.

Mark
  • 18,730
  • 7
  • 107
  • 130
  • I think that's a duplicate. If you want to consume a field of a `struct`, you must consume the `struct` as well (otherwise it would have a field with "corrupted" memory) – Boiethios May 25 '18 at 19:02
  • There has to be some kind of limit or workaround, right? Otherwise your whole object structure gets consumed every time you consume any field. – Mark May 25 '18 at 19:08
  • Think about this: how could you move a field without moving the whole struct? – Boiethios May 25 '18 at 19:16
  • Possible duplicate of [How to move one field out of a struct that implements Drop trait?](https://stackoverflow.com/questions/31307680/how-to-move-one-field-out-of-a-struct-that-implements-drop-trait) – Boiethios May 25 '18 at 19:18
  • Thanks for the hints with `replace`, I've tried using `replace(&mut self.reader_or_delegate, ReaderOrDelegate::Delegate(AnotherParser { reader }));` instead. The problem however persists: I cannot create `AnotherParser` because `reader` is already borrowed by the `match`. – Mark May 25 '18 at 19:28
  • Oh this point is just an issue because you do not have the *non lexical lifetimes*. If you use a nightly compiler, just add `#![feature(nll)]` and that's ok – Boiethios May 25 '18 at 19:34
  • Thanks, that helped a lot, only the one error now. The problem remains that I need to move the `reader` in some cases, but mutably borrow it in others. Updated the code. – Mark May 25 '18 at 19:51

1 Answers1

1

Consider the line:

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

You need a reader value, not a reference, to build 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`

But it is not possible to obtain such value. If we try:

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

We get now the true problem with your design:

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

The parse method borrows self and this imply that at this point we cannot move out owned values from a borrowed self.

Note also that the error E0507 also holds for the line:

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

because consume try to move out a value from a borrowed delegate.

In your code the compiler does not show this problem, but it will emerge if you comment out the line that apparently is the only problem remaining in your example.

The only solution that I'm able to arrange without incurring into borrow checker errors and without transforming too much your design is based on using a reference counter Reader, shared between the top level parser and the delegate parser.

With Rc<Reader> You have only one reader shared through a smart pointer with your parsers.

ReadOrDelegate enum gives just indication of the active parser, no more owned reader to move around.

#![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
    }
}
attdona
  • 17,196
  • 7
  • 49
  • 60
  • Thanks. I was afraid that'd be the only way. It's probably good enough in practise to be honest, but its nice theoretically to have only one reader... – Mark May 27 '18 at 08:04
  • You have only one reader, shared with all parsers through a Rc pointer – attdona May 27 '18 at 08:51
  • Okay I should have said "to have only one lexer that can read" – Mark May 27 '18 at 08:53