如何在Rust中正确表示基于堆栈的语言?

时间:2018-10-18 19:35:54

标签: rust

我正在尝试模仿Parsing a simple imperative language(Haskell)的第3节。特别是,我正在考虑使用一种基于堆栈的语言,而不是一种命令式语言,并且我正在尝试纠正惯用的Rust代码来表示数据。

假设您要制作一个很小的(非常小的stack based language,它具有一些基本的算术运算,没有用户定义的函数,并且可以处理十进制数字和整数。例如:

   1 2 +
-> Stack contains: 3

这是怎么回事?从左到右读取,将1和2压入堆栈,+将压下12,然后压下3= 1 + 2 )放入堆栈。

我的想法是考虑需要解析的3类型的“原语”。您有整数,十进制数字和函数。此外,十进制和整数都是“名词”,函数是“动词”。因此,在执行程序时,我的主意是您可以通过扩展Result<T, E>枚举的主意在Rust中表示这些主意。我想出了以下方案:

enum Noun {
    Integer(i64),
    Decimal(f64)
}

enum Primitive<T> {
    Noun(T),
    Verb(Fn(Vec<Noun>) -> Noun),
}

// Not really important, just giving a main so it can be ran
fn main() {
    println!("Hello, world!");
}

换句话说,原语是NounVerb,而Noun是整数或浮点数。

但是,结果是:

error[E0277]: the trait bound `std::ops::Fn(std::vec::Vec<Noun>) -> Noun + 'static: std::marker::Sized` is not satisfied
 --> main.rs:8:10
  |
8 |     Verb(Fn(Vec<Noun>) -> Noun),
  |          ^^^^^^^^^^^^^^^^^^^^^^ `std::ops::Fn(std::vec::Vec<Noun>) -> Noun + 'static` does not have a constant size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `std::ops::Fn(std::vec::Vec<Noun>) -> Noun + 'static`
  = note: only the last field of a struct may have a dynamically sized type

error: aborting due to previous error(s)

在Rust中执行此操作的标准方法是什么?

1 个答案:

答案 0 :(得分:1)

类型Fn(Vec<Noun>) -> Noun描述一个特征对象,它是表示该特征的任何类型的占位符。由于特征可以通过捕获附加变量的普通函数或闭包来实现,因此编译器无法知道为此类对象分配多少空间。特征对象是“动态调整大小”的,因此不能存在于堆栈中。

解决该错误消息的一种方法是将其存储在堆中:

enum Primitive<T> {
    Noun(T),
    Verb(Box<dyn Fn(Vec<Noun>) -> Noun>),
}

dyn关键字明确表明我们正在处理一个trait对象,并且对该对象的方法调用是动态调度的。在当前的Rust中它是可选的,但在新代码中建议使特征对象更明显。

一种替代方法是使用普通函数指针而不是特征对象:

enum Primitive<T> {
    Noun(T),
    Verb(fn(Vec<Noun>) -> Noun),
}

一个函数指针只能指向一个普通函数或一个不能捕获任何变量的闭包,因此它是静态大小的,因此可以存储在堆栈中。

我个人可能会实现一个名为Function或类似名称的自定义特征,并使用

enum Primitive<T> {
    Noun(T),
    Verb(Box<dyn Function>),
}

自定义特征将为您提供更大的灵活性,以便将元数据和其他方法附加到特征对象,例如一种检索函数将消耗的输入数量的方法。