如何在Rust中复刻C语言struct inheritance可变大小AST节点实现
完全可以复刻该C语言中的struct inheritance实现模式,核心依赖Rust的#[repr(C)]属性强制内存布局符合C规范,具体实现如下:
基础类型定义
首先定义标签枚举和基结构体,和C代码的逻辑完全对应:
// 对应C代码的Ast_Tag,#[repr(u32)]保证枚举内存大小和C的enum一致 #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u32)] enum AstTag { Ident, String, } // 对应C代码的Ast基结构体,必须加#[repr(C)]保证字段顺序和C一致 #[repr(C)] struct Ast { tag: AstTag, flags: u32, } // 示例内部字符串类型,替换为你实际的Intern实现即可 struct Intern; // 对应C代码的Ast_Ident派生结构体,首字段为基结构体Ast #[repr(C)] struct AstIdent { base: Ast, name: *mut Intern, } // 对应C代码的Ast_String派生结构体 #[repr(C)] struct AstString { base: Ast, str: *mut Intern, }
上转(派生类转基类)实现
对应C代码中bar函数的向上转型逻辑,Rust中可直接通过指针转换实现,完全符合C的行为:
// 对应C代码的bar函数 fn bar(ident: *mut AstIdent) { // 两种转换方式都合法: // 方式1:直接取基字段的指针,逻辑更直观 let base_node = unsafe { &mut (*ident).base } as *mut Ast; // 方式2:直接强转派生指针为基指针,因为#[repr(C)]保证首地址重合 // let base_node = ident as *mut Ast; foo(base_node); }
下转(基类转派生类)实现
对应C代码中foo函数的向下转型逻辑,和C一致先判断标签再做转换,转换操作需要放在unsafe块中:
// 示例处理函数,替换为实际业务逻辑即可 fn do_something_with_name(val: *mut Intern) { // 业务逻辑实现 } // 对应C代码的foo函数 fn foo(node: *mut Ast) { unsafe { match (*node).tag { AstTag::Ident => { let ident_node = node as *mut AstIdent; do_something_with_name((*ident_node).name); } AstTag::String => { let str_node = node as *mut AstString; do_something_with_name((*str_node).str); } } } }
关键注意事项
- 所有涉及继承的结构体必须标注
#[repr(C)],Rust默认的内存布局不保证字段顺序,不加该属性会导致指针转换出现未定义行为 - 上下转型都需要使用
unsafe块,类型匹配的校验逻辑需要你自行保证(和C语言的要求完全一致,需要保证tag字段和实际的结构体类型对应) - 该方案完全满足不同AST节点内存大小不同的需求,不会出现枚举类型所有变体占用最大内存的问题,1:1复刻了C语言的struct inheritance实现逻辑
内容的提问来源于stack exchange,提问作者zagortenay333
相关产品推荐
相关产品推荐

