Sway 枚举内存布局深度解析:tag 标记与变体联合存储原理
【免费下载链接】sway🌴 Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway
导读
本文深入讲解 Sway 语言中枚举(enum,又称"和类型")在内存中的真实存储方式:Sway 编译器为每个枚举值附加一个 8 字节的 tag(标签)用于区分当前表示的是哪一个变体,而 tag 之后预留的空间则等于所有变体中尺寸最大者。全文以 官方参考文档 的两个典型示例为骨架,结合 示例源码 与 IR 层实现(sway-ir/src/irtype.rs)逐字节剖析,读完你不仅能准确算出任意枚举的内存占用,还能理解为什么单元变体与最大变体会"占用同样空间"这一 Sway 语言的重要特性。
枚举内存布局的核心规则
Sway 中的枚举是一种和类型(sum type),运行时必须能够区分当前存储的是哪一个变体。为此,Sway 在枚举的内存表示上采用了带标签的联合(tagged union)这一经典策略:
- tag 字段:一个 1 字(word)即8 字节的标签,记录当前枚举实例是第几个变体;
- 数据区:紧随 tag 之后,其大小不是当前变体的大小,而是所有变体中尺寸最大的那一个。
因此计算一个枚举类型内存大小的公式非常简单:
枚举内存大小 = 8 字节(tag)+ 最大变体的字节数这一规则的含义值得特别注意:无论你实例化的是哪个变体,该枚举值在内存中占用的空间都是相同的。即使用最小、甚至不携带任何数据的单元变体(unit variant),其实际占用也是"最大变体的空间",因为编译器在编译期无法预知运行时到底存哪个变体,必须按最坏情况(最大变体)预留空间。
从 Sway 的 IR(中间表示)实现可以印证这一设计。在 sway-ir/src/irtype.rs 中,枚举类型被明确描述为:
Enums are at the moment represented as structs with two fields, first one being the tag and second the union of variants.
即枚举在 IR 中被表示为一个包含两个字段的结构体:第一个字段是 tag,第二个字段是所有变体的联合(union)。IR 层正是通过"结构体有两个字段、且第一个字段是整数类型、第二个字段是联合类型"这一特征来识别枚举的(见 irtype.rs)。这一实现事实与文档"8 字节 tag + 最大变体空间"的表述完全吻合。
示例一:u64 变体与单元变体(内存 16 字节)
枚举定义
第一个示例定义了一个包含两个变体的枚举T,完整源码位于 docs/reference/src/code/misc/advanced-concepts/enum_advanced/src/lib.sw:
pub enum T { a: u64, b: (), }其中:
- 变体
a的类型是u64,即 64 位无符号整数,占用8 字节。关于u64等数值类型的语义可参考 内置数值类型文档; - 变体
b的类型是单元类型(),不携带任何数据,占用0 字节。
内存大小计算
最大变体是u64(8 字节),因此:
枚举 T 的大小 = 8 字节(tag)+ 8 字节(u64)= 16 字节实例化两个变体的实际占用
文档强调了一个反直觉的事实:无论实例化哪个变体,占用都是 16 字节。
实例化携带数据的变体T::a(42),示例代码见 lib.sw:
fn u64_type_space() { let a = T::a(42); }实例化不携带数据的单元变体T::b,示例代码见 lib.sw:
fn u64_unit_space() { let b = T::b; }两者在内存中均占用16 字节:8 字节 tag + 8 字节 u64 数据区。对于T::b而言,数据区虽然实际没有用到,但空间依然被预留——这正是"和类型"按最大变体对齐的代价,也是使用枚举时必须意识到的内存开销。
示例二:b256 变体与 u64 变体(内存 40 字节)
枚举定义
第二个示例的枚举K同样包含两个变体,见 lib.sw:
pub enum K { a: b256, b: u64, }这里引入了一个新类型b256:Sway 中唯一的"字节"类型,固定包含256 位即 32 字节的信息,且被当作一个整体类型对待,而非可迭代的字节数组(参见 b256 内置类型文档)。
内存大小计算
最大变体是b256(32 字节),因此:
枚举 K 的大小 = 8 字节(tag)+ 32 字节(b256)= 40 字节实例化两个变体的实际占用
实例化b256变体,需要传入一个 256 位的十六进制字面量,示例见 lib.sw:
fn b256_type_space() { let a = K::a(0x0000000000000000000000000000000000000000000000000000000000000000); }实例化u64变体,示例见 lib.sw:
fn b256_unit_space() { let b = K::b(42); }与示例一完全一致,两者均占用40 字节:8 字节 tag + 32 字节 b256 数据区。K::b(42)虽然只需要 8 字节存数据,但因为枚举布局必须容纳最大的b256变体,实际依然占据 40 字节。
变体数量更多的枚举如何计算
虽然两个示例都只包含两个变体,但上述规则可以自然推广到任意数量变体的枚举:
枚举大小 = 8 字节(tag)+ max(所有变体的大小)无论枚举有几个变体,tag 始终只有 1 个(8 字节),用于区分"当前是第几个变体";数据区始终只取最大变体的大小。因此,在设计枚举时,若某个变体携带超大类型(如b256、大结构体或数组),整个枚举的内存占用都会被抬高,所有其他变体(包括单元变体)的实例化都会付出同样的空间成本。这是编写高效 Sway 智能合约时需要权衡的要点之一。
验证与运行
仓库为本文两个示例提供了完整的可运行工程,位于 docs/reference/src/code/misc/advanced-concepts/enum_advanced,其 Forc.toml 中声明了项目名为enum-advanced,以 src/lib.sw 为入口,并依赖仓库本地的std标准库:
[project] authors = ["Fuel Labs <contact@fuel.sh>"] entry = "lib.sw" license = "Apache-2.0" name = "enum-advanced" [dependencies] std = { path = "../../../../../../../sway-lib-std" }你可以通过 Sway 官方构建工具forc对该示例进行编译检查:
forc build在仓库根目录执行forc build --path docs/reference/src/code/misc/advanced-concepts/enum_advanced即可验证该枚举定义与实例化代码能通过编译。若想进一步观察编译产物,可使用forc build --print-ir或forc build --print-asm查看 IR 与汇编,直观看到枚举被拆解为"tag + 联合"的低层表示。
小结
- Sway 枚举采用tagged union布局:
8 字节 tag + 最大变体空间; - 所有变体的实例化占用相同内存,即使是不携带数据的单元变体;
u64(8 字节)变体的枚举占用 16 字节;b256(32 字节)变体的枚举占用 40 字节;- IR 层将枚举表示为"tag(整型字段)+ 变体联合"的双字段结构体(irtype.rs),与文档规则一一对应;
- 设计枚举时,最大变体的尺寸决定整个枚举的空间成本,应在合约开发中予以考量。
延伸阅读:本文讨论的是枚举的内存布局细节,若需回顾枚举的语法、枚举套枚举、枚举嵌套结构体等基础用法,可参阅 内置类型之枚举 及 examples/enums 下的配套示例。
【免费下载链接】sway🌴 Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考