【问题标题】:Borrow checker problems for parser that can delegate可以委托的解析器的借用检查器问题
【发布时间】:2018-05-25 18:32:19
【问题描述】:

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

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

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

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

我创建了一个最小的示例,并包含了 cmets 关于 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 只需要一个所有者。

编辑:对我来说,replace 的问题如何通过“嵌套”应用于这种情况(reader 已经被match 借用然后想要交换一个字段)。因此,虽然可能相关,但我认为另一个问题不足以解决这个问题。反正不适合我。

EDIT 2:在代码示例和错误中包含评论建议。

【问题讨论】:

  • 我认为这是重复的。如果你想使用 struct 的字段,你也必须使用 struct (否则它会有一个带有“损坏”内存的字段)
  • 必须有某种限制或解决方法,对吧?否则每次使用任何字段时都会消耗整个对象结构。
  • 想一想:如何在不移动整个结构的情况下移动字段?
  • 感谢replace 的提示,我尝试改用replace(&mut self.reader_or_delegate, ReaderOrDelegate::Delegate(AnotherParser { reader }));。但问题仍然存在:我无法创建 AnotherParser,因为 reader 已被 match 借用。

标签: delegates rust pattern-matching borrow-checker ownership


【解决方案1】:

考虑这条线:

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 枚举只给出了活动解析器的指示,没有更多拥有的阅读器可以移动。

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

【讨论】:

  • 谢谢。我怕那是唯一的办法。老实说,这在实践中可能已经足够好了,但理论上只有一个读者很好......
  • 你只有一个阅读器,通过一个 Rc 指针与所有解析器共享
  • 好吧,我应该说“只有一个可以阅读的词法分析器”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多