news 2026/9/12 16:31:56

Sway 枚举内存布局深度解析:tag 标记与变体联合存储原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Sway 枚举内存布局深度解析:tag 标记与变体联合存储原理

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-irforc 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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 16:29:19

LLVM嵌入式工具链源码深度评测:从模块划分到构建测试实战

嵌入式圈子这两年讨论 LLVM Embedded Toolchain for Arm 的人越来越多了。作为长期用 ARM Compiler 5/6 做 Cortex-M 项目的老用户&#xff0c;我一开始对 LLVM 工具链是持观望态度的。直到有一次项目组要把老代码从 Keil 环境整体搬到 CI 流水线&#xff0c;编译速度、许可证和…

作者头像 李华
网站建设 2026/9/12 16:28:58

加入 RISC-V 通知小组:参与 rustc 的 RISC-V 支持诊断与测试

加入 RISC-V 通知小组&#xff1a;参与 rustc 的 RISC-V 支持诊断与测试 【免费下载链接】rust Empowering everyone to build reliable and efficient software. 项目地址: https://gitcode.com/GitHub_Trending/ru/rust RISC-V 通知小组&#xff08;notification grou…

作者头像 李华