如何在 Rust 结构中存储动态类型变量?

How to store a dynamically typed variable in a Rust struct?

我否认我是 Rust 的初学者,这是我的第一个项目。我正在尝试用 Rust 编写 SQL 编译器。我必须创建一个 Token 结构来表示 SQL 查询的语法单元。在 C/C++ 我会写这样的结构:

typedef unsigned long Value;

// Represents the type of the token (e.g. number, string, identifier...)
typedef enum class TokenType { ... }

struct Token {
  TokenType type;
  unsigned char priority;
  Value value;
}

使用这种方法,我使用 Value 类型来存储任何类型的值。如果它是一个数字,value 被解释为一个整数,浮点数...,而当 value 表示一个字符串或标识符时,它被转换为一个字符串指针,然后用于访问堆-它指向的已分配数据。
为了决定如何处理 Tokenvalue 属性,我使用 type 字段。例如,如果 type 告诉我令牌是一个整数,我就将 value 转换为一个整数。如果 type 告诉我令牌是标识符,我将 value 转换为字符串指针。

我的问题是,是否可以在安全 Rust 中做类似的事情?如果不是,那么实现可以基于 TokenType 以多种方式解释的动态类型变量的好方法是什么?或者是否有更简洁的方法来创建在 Rust 中运行良好的语法标记?

提前致谢。

在 Rust 中,如果你有一组可能的类型,你应该使用 enum。 Rust 中的枚举与 C 中的不同:它们可以携带有效负载(即它们是求和类型)。

enum TokenType {
    Number(i32),
    Identifier(String),
    String(String),
    Plus,
    Minus,
    // ...
}

然后match反对:

let e: TokenType = ...;
match e {
    TokenType::Number(n) => println!("number: {n}"),
    TokenType::String(s) => println!("string: {s}"),
    TokenType::Plus => println!("plus"),
    // ...
}
// some variants can hold parsed data
pub enum TokenKind {
    Ident,
    Op,
    Int(i64),
    Float(f64),
    String,
    Eof,
}

pub struct Source {
    pub content: String,
}

impl Source {
    // no need to allocate a new string for each token, just store the span
    // and access the slice when needed
    pub fn get_underlying_string(&self, token: &Token) -> &str {
        &self.content[token.span.clone()]
    }   
}

// we use struct because token has some common parts
pub struct Token {
    pub kind: TokenKind,
    pub span: Range<usize>,
}

fn main() {
    let mut token = Token {
        kind: TokenKind::Int(10),
        span: 0..1,
    };

    println!("int: {}", match token.kind {
        TokenKind::Int(i) => i,
        _ => panic!("unexpected token kind"),
    }); // prints "int: 10"
}

在 Rust 中,这个问题有更好的解决方案。

Rust 的 enum 工作方式有点不同,但它们更适合这种情况:枚举变体本身可以具有仅适用于枚举的一个变体的字段。

这样,您可以将值 存储在 您的枚举中:

struct Token{
  priority: u8,
  value: TokenValue
}

enum TokenValue{
  Int (i32),
  Str (String),
  Id {
    name: String,
    id: u32
  }
  // ... other variants
}

这也为使用它的代码提供了类型安全,Rust 可以优化它为每个变体分配的内存量。

这个功能解释的很好in The Rust Programming Language book