我正在尝试编写一个简单的 IR 解释器。我知道如何使用 IR 和返回类型的枚举来执行此操作,但有几个原因我不想这样做:
impl
,当 an 中的所有内容都enum
属于同一类型时,这会受到限制因此,我非常希望摆脱由特征表示的 IR 和返回数据,例如:
trait Data{}
trait IR{
fn evaluate(self) -> Box<dyn Data>;
}
Run Code Online (Sandbox Code Playgroud)
但我在尝试进行评估时遇到了障碍:
struct Bool(bool);
impl Data for Bool{}
//other things impl Data, of course
struct IfThenElse<T1, T2, T3>{
condition: T1,
then_branch: T2,
else_branch: T3
}
impl<T1, T2, T3> IR for IfThenElse<T1, T2, T3> where T1: IR, T2: IR, T3: IR{
fn evaluate(self) -> Box<dyn Data>{
let condition_evaluated = self.condition.evaluate();
todo!(); //Uh oh: That's a Box<dyn Data>. It's reasonable to fail if it isn't a Bool, but how do I check that?
}
}
Run Code Online (Sandbox Code Playgroud)
基本上,我需要能够使用返回类型执行特定的操作。在多种情况下,这是必要的 - IR 包括模式匹配、记录字段访问和函数应用,如果无法检查子节点的计算返回类型,所有这些都可能有条件地失败。
我可以想出很多解决这个问题的方法,但每种方法都遇到了不同的实现障碍,或者至少是混乱的:
Any
和沮丧enum
包含每个特征的所有变体,以及来自和到特征实现结构的函数Data
以返回特定的具体类型!
某些类型的类型参数,有效地将它们从这些类型中排除我可以针对每个问题提出单独的问题,但我认为我更有可能错过了一种更基本和标准的方法。我试图找到 Rust 解释器的示例来寻找标准,但我发现的大部分内容要么是关于解析/词法分析,要么是针对比我需要的更复杂和性能优化的解决方案(尤其是第一次通过)。
特质模型有没有一种优雅的方法来解决这个问题?如果不是,我应该如何表示数据和 IR?
特质的价值是什么Data
?你期望它有任何方法,还是纯粹是一个标记特征?
如果IR
实际上是“可以评估为数据的东西”,这听起来很像“数据”是特征的关联类型:
trait IR {
type Output;
fn evaluate(self) -> Self::Output;
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以限制IfThenElse
实现,要求条件实际上计算为布尔值(并且两个分支具有相同的输出类型):
impl<T1, T2, T3> IR for IfThenElse<T1, T2, T3>
where
T1: IR<Output = bool>,
T2: IR,
T3: IR<Output = T2::Output>,
{
type Output = T2::Output;
fn evaluate(self) -> Self::Output {
if self.condition.evaluate() {
self.then_branch.evaluate()
} else {
self.else_branch.evaluate()
}
}
}
Run Code Online (Sandbox Code Playgroud)
它还避免了Box
ed 特征对象,这似乎是不必要的。
如果您发现仍然需要一个必须由任何Data
IR 评估的输出实现的特征,那么您可以将其添加为关联类型的特征绑定。
trait IR {
type Output: Data;
// ...
}
Run Code Online (Sandbox Code Playgroud)