Проблемы с проверкой заимствования для парсера, который может делегировать - PullRequest
0 голосов
/ 25 мая 2018

У меня есть несколько парсеров.Существует элемент верхнего уровня, который может делегировать другому.

Parser s получают свои данные от Reader (изменяемый).Я хочу, чтобы только один Parser мог анализировать одновременно, только у одного парсера должен быть 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`

Я думаю, что, вероятно, я могу решить эту проблему, взяв reader из ReaderOrDelegate и поместив его в каждый анализатор как Rc<RefCell<Parser>>>.Но я думаю, что иметь его в перечислении более логично: за один раз должен быть только один парсер, способный использовать ридер.Возможно ли это?

Я понимаю, что ошибки имеют смысл в этом случае, но я чувствую, что на высоком уровне должна быть возможность делать то, что я хочу.Для Reader.

EDIT нужен только один владелец. Мне кажется, нетривиально, как вопрос с replace может быть применен к этому случаю с помощью 'nesting''(reader уже занято match, а затем хотите поменять поле).Так что, возможно, это связано, но я не думаю, что этого достаточно, чтобы решить этот вопрос.В любом случае, не для меня.

РЕДАКТИРОВАТЬ 2 : Включены предложения комментариев в примере кода и ошибка.

1 Ответ

0 голосов
/ 27 мая 2018

Рассмотрим строку:

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> У вас есть только один читатель, совместно используемый интеллектуальным указателем с вашими синтаксическими анализаторами.

ReadOrDelegate enum просто указывает на активный анализатор, больше не принадлежащий читателюпередвигаться.

#![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
    }
}
...