Как правильно представлять язык на основе стека в Rust? - PullRequest
0 голосов
/ 18 октября 2018

Я пытаюсь эмулировать раздел 3 из Разбор простого императивного языка (Haskell).В частности, я рассматриваю язык, основанный на стеке, а не императивный язык, и пытаюсь исправить идиоматический код Rust для представления данных.

Предположим, вы хотите сделать маленький ( действительно маленьким *)1006 *) язык на основе стека , который имеет некоторые основные арифметические операции, не имеет пользовательских функций и работает с десятичными числами и целыми числами.Так, например:

   1 2 +
-> Stack contains: 3

Что происходит?Читайте слева направо, нажмите 1 и 2 в стеке, и + вытолкнет 1 и 2, а затем вставит 3 (= 1 + 2) в стек.

Моя идея состоит в том, чтобы рассмотреть 3 типов "примитивов", которые нуждаются в разборе.У вас есть целые числа, десятичные числа и функции.Кроме того, десятичные и целые числа являются «существительными», а функции - «глаголами».Поэтому при выполнении программы моя идея состояла в том, чтобы вы могли представить эти идеи в Rust, расширив идею перечисления Result<T, E>.Я придумал следующую схему:

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!");
}

Другими словами, примитив - это либо Noun, либо Verb, а 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 голосов
/ 18 октября 2018

Тип Fn(Vec<Noun>) -> Noun описывает объект черты , заполнитель для любого типа, реализующего эту черту.Поскольку эта черта может быть реализована с помощью простых функций или замыканий, которые захватывают дополнительные переменные, компилятор не может знать, сколько места выделить для такого объекта.Объект признака имеет «динамический размер» и поэтому не может существовать в стеке.

Один из способов устранения сообщения об ошибке - вместо этого сохранить в куче:

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

Ключевое слово dyn явно указывает, что мы имеем дело с объектом-признаком, и что вызовы методов для этого объекта отправляются динамически.В текущем Rust это необязательно, но рекомендуется в новом коде сделать объекты признаков более очевидными.

Альтернативой является использование простого указателя функции вместо объекта свойства:

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

ФункцияУказатель может указывать только на обычную функцию или замыкание, которое не захватывает какие-либо переменные, и поэтому имеет статический размер, поэтому его можно хранить в стеке.

Лично я, вероятно, реализовал бы пользовательскую черту под названиемFunction или аналогичный, и используйте

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

Настраиваемая черта даст вам гораздо больше гибкости, чтобы прикрепить метаданные и дополнительные методы к объекту черты, например, метод для получения количества входов, которые будет выполнять функцияпотребляют.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...