首页 > 解决方案 > 结构继承

问题描述

C 中的典型 AST 实现可能如下所示:

typedef enum {
    AST_IDENT,
    AST_STRING,
} Ast_Tag;

typedef struct { Ast_Tag tag; u32 flags; } Ast;
typedef struct { Ast base; Intern *name; } Ast_Ident; 
typedef struct { Ast base; Intern *str;  } Ast_String;

// A function that downcasts to the derived type:
void foo (Ast *node) {
    switch (node->tag) {
    case AST_IDENT: {
        Ast_Ident *ident = (Ast_Ident*)node;
        do_something_with_name(ident->name);
    } break;

    case AST_STRING: {
        Ast_String *str = (Ast_String*)node;
        do_something_with_name(str->name);
    } break;
    }
}

// A function that upcasts to the base type:
void bar (Ast_Ident *ident) {
    foo((Ast*)ident);
}

有没有办法在生锈中做到这一点?我想向下转换会特别成问题。

注意:我不是在问如何实现 AST,而是在问如何复制结构继承,如上所示。

编辑:很抱歉造成混乱。这里的要点是使用结构继承是为了避免使每个节点都具有相同的大小,因此枚举不是一种选择。

标签: rust

解决方案


你基本上实现了 Rustenum变体:

struct Ast {
    flags: u32,
    kind: AstKind,
}

enum AstKind {
    Ident(String),
    String(String),
}

fn foo(ast: Ast) {
    match ast.kind {
        AstKind::Ident(ident) => println!("flags: {:?}, ident: {:?}", ast.flags, ident),
        AstKind::String(s) => println!("string: {:?}, ident: {:?}", ast.flags, s),
    }
}

fn bar(flags: u32, ident: String) {
    foo(Ast {
        flags,
        kind: AstKind::Ident(ident),
    })
}

fn main() {
    bar(42, "MisterMV".into())
}

您似乎关心大小:

这里的要点是使用结构继承是为了避免使每个节点都具有相同的大小,因此枚举不是一种选择。

这通常不是问题,clippy 有一个 lint 调用large_enum_variant,有一个合理的默认值,您可以更改它。Clippy 也提出了一个合理的解决方案:

enum AstKind {
    Ident(Box<Big>),
    String(Small),
}

还有其他答案已经涵盖的其他选择。


推荐阅读