news 2026/9/12 5:02:26

Sway 变量详解:不可变默认、可变声明与类型注解机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Sway 变量详解:不可变默认、可变声明与类型注解机制

Sway 变量详解:不可变默认、可变声明与类型注解机制

【免费下载链接】sway🌴 Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway

导读

本文基于 Sway 官方文档 docs/book/src/basics/variables.md 展开,系统讲解 Sway 语言中变量的核心规则:默认不可变性let/let mut声明语法、类型注解(type annotation)以及编译器对类型冲突的强制校验。Sway 是 Fuel 生态的智能合约语言,其变量语义直接影响合约的安全性与可预测性——默认不可变从语言层面杜绝了"意外改状态"这类隐患,理解这套机制是编写可靠、高效智能合约的第一步。读完本文,你将掌握 Sway 变量的完整声明方式、赋值求值顺序、类型注解用法,并能从编译器源码层面理解不可变变量为何"改不动"。

变量在 Sway 中默认不可变

Sway 中的变量默认是不可变的(immutable by default)。这意味着:默认情况下,一旦变量被声明,它的值就不能再改变。这是 Sway 鼓励安全编程的方式之一,如今许多现代语言(如 Rust)也采用了相同的默认设计。

不可变默认并不是"不能可变",而是"需要显式声明才可变"。这种设计将可变性变成一种需要刻意为之、可以被审查的选择,从而在代码评审和审计时更容易定位潜在的状态变更点,对智能合约这种"一旦上链难以回滚"的场景尤为重要。

声明一个变量

最基本的变量声明使用let关键字:

let foo = 5;

上面这条语句声明了变量foo,关于它我们可以得出三点结论:

  1. 它是不可变的;
  2. 它的值是5
  3. 它的类型是u64,即 64 位无符号整数。

其中u64是 Sway 的默认数值类型。为什么会默认u64?因为 FuelVM 的字长(word size)是 64 位,见 docs/book/src/basics/built_in_types.md 的说明:使用更小的数值类型能节省的空间微乎其微,因此编译器在无法推断更精确类型时回退到u64。Sway 的原始数值类型还包括u8u16u32u256,但没有有符号整数——在区块链领域,浮点数和负数用途有限,因此其实现被留给特定用例的库来处理。

声明可变变量:let mut

要让变量可变,需要使用mut关键字:

let mut foo = 5; foo = 6;

此时foo是可变的,重新赋值为6是合法操作——我们被允许**修改(mutate)**变量foo来改变它的值。

mut关键字不仅适用于顶层变量,也适用于数组、元组等复合数据的内部元素,前提是承载它们的变量本身被声明为mut。例如仓库示例 examples/arrays/src/main.sw 中展示了可变数组的元素修改:

let mut array_of_bools: [bool; 2] = [true, false]; assert(array_of_bools[0]); // 修改数组元素 array_of_bools[1] = true; assert(array_of_bools[1]);

同样的规则适用于元组,见 examples/tuples/src/main.sw:

let mut tuple2 = (5, true, ("Sway", 8)); // 单独修改元组内部元素 tuple2.1 = false; // 或者一次性整体重新赋值(必须保持相同的数据类型) tuple2 = (9, false, ("Fuel", 99));

赋值时的求值顺序

向可变变量赋值时,右侧(RHS)的表达式会先于左侧(LHS)求值。以下面这段代码为例:

let mut array = [0, 0, 0]; let mut i = 0; array[i] = { i += 1; i };

执行过程是:先求值花括号块,块内先把i自增为1,再以i(值为1)作为块的返回值,然后才把这个1写入array[1],最终array变为[0, 1, 0]。理解"先 RHS 后 LHS"的顺序,对写出可预测的链上逻辑很重要——例如在索引同时被修改的循环或状态更新场景中,它决定了写入的位置。

类型注解

变量声明中可以包含类型注解(type annotation),其作用是在值之外,显式声明变量的类型:

let foo: u32 = 5;

这里我们把变量foo的类型显式声明为u32(32 位无符号整数)。再看几个其他类型的注解示例:

let bar: str[4] = __to_str_array("sway"); let baz: bool = true;
  • barstr[4],即长度为 4 的定长字符串数组。由于字符串字面量在 Sway 中天然是可变长字符串切片(str),因此需要用编译期转换函数__to_str_array将其转为定长字符串数组;
  • baz是布尔类型bool,仅能取值truefalse

完整的原始类型清单见 docs/book/src/basics/built_in_types.md,包括()(单元类型)、u8u256整数族、str[]/str字符串族、bool以及b256(256 位哈希)。

类型冲突:编译器的强制校验

如果声明值无法赋值给声明的类型,编译器会生成错误。

例如文档中提到的let w: (u64) = (42,);(把一元元组赋值给普通u64类型)就是典型冲突;反过来,let x: u64 = (42);let y: (u64) = (42);中的(42)都会被推断为普通u64,而只有带尾随逗号的(42,)才构成真正的一元元组,相关规则详见 docs/book/src/basics/built_in_types.md。

类型注解的另一重要作用是配合类型推断:Sway 是静态类型语言,编译期必须知道每个值的类型,但通常不需要你逐个标注——编译器会自行推断,见 docs/book/src/basics/built_in_types.md。从编译器实现看,变量声明的类型检查逻辑位于 sway-core/src/semantic_analysis/ast_node/declaration/variable.rs:在TyVariableDecl::type_check中,编译器先解析类型注解(type_ascription),再对右侧表达式做类型检查(ty::TyExpression::type_check),随后通过type_engine.unify统一两侧类型——若两侧无法统一,就会报出类型冲突错误。

不可变变量的底层保障

"默认不可变"并不是文档层面的约定,而是被编译器强制执行的规则。仓库中的错误定义 sway-error/src/error.rs 明确列出了违反规则时的报错信息:

Assignment to an immutable variable. Variable "{decl_name} is not declared as mutable.

也就是说,当你写出foo = 6;foo并未以let mut声明时,编译器会直接拒绝编译并提示该变量未声明为可变。与可变性相关的错误还包括(见 sway-error/src/error.rs 与 sway-error/src/error.rs):

  • Cannot pass immutable argument to mutable parameter.(不可变实参不能传给可变参数);
  • Reference to a mutable value cannot reference an immutable variable.&mut引用不能指向不可变变量)。

这些约束共同构成了一套完整的可变性安全检查:从普通赋值、函数传参到引用借用,不可变变量都无法被"绕道修改"。在语义分析阶段,变量声明的可变性会被记录为ty::VariableMutability::new_from_ref_mut(false, var_decl.is_mutable)(见 variable.rs),后续的赋值、借用检查都以此为依据。

变量与常量、存储变量的区分

理解变量时,还需注意它与 Sway 中另外两类"值载体"的区别:

  • 常量(const:与变量类似,但始终在编译期求值,且mut关键字不能用于常量。常量既可在函数内声明,也可在全局或impl作用域声明,详见 docs/book/src/basics/constants.md。相关示例见 examples/configurable_constants/src/main.sw。
  • 存储变量(storage variables):智能合约的持久化状态,通过storage关键字声明并配合StorageMapStorageVec等结构使用,其可变性由存储模型决定,与普通局部变量的mut语义不同。可参考 examples/advanced_storage_variables/src/main.sw、examples/storage_vec/src/main.sw 等示例。

实战要点小结

  1. 能不可变就不可变:默认使用let,仅在确实需要修改时才加mut,让状态变更在代码中一目了然;
  2. 显式注解类型:当类型推断不直观(例如整数字面量默认按u64处理)或希望强制窄类型(如u32)时,使用let x: u32 = ...;
  3. 记住"先 RHS 后 LHS":在赋值表达式中,右侧副作用先于写入发生;
  4. 依赖编译器兜底:类型冲突、对不可变变量赋值、把不可变值传给&mut参数等错误都会被编译期拦截,善用错误信息定位问题。

掌握 Sway 变量的这些规则,是进一步学习 控制流、函数 与 结构体、元组和枚举 等主题的基础。

【免费下载链接】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 5:01:35

MAVLink协议详解:从帧结构到飞控二次开发实战

先别急着翻代码、装环境,聊这个题目之前,我建议你先想明白一个问题:一台无人机飞起来,飞控、遥控器、地面站、任务电脑之间到底在用什么“话”交流?如果答案是“串口数据”“遥控PWM波”“地面站图形”,那说…

作者头像 李华
网站建设 2026/9/12 4:59:44

如何3分钟让同事在手机上预览文档:kkFileView 移动端实战

如何3分钟让同事在手机上预览文档:kkFileView 移动端实战 【免费下载链接】kkFileView Universal File Online Preview Project based on Spring-Boot 项目地址: https://gitcode.com/GitHub_Trending/kk/kkFileView 你还在群里发文件,然后等对方…

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

Python实现图片批量转PDF的高效方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 4:57:29

STM32F4+FreeRTOS+ESP8266多任务物联网系统实战

简介:本资源是一套完整的基于STM32FreeRTOSESP8266的实时天气系统毕业设计/课程设计项目,面向嵌入式初学者与高校电子类专业学生,解决物联网终端开发中多任务调度、Wi-Fi联网通信与本地人机交互集成等典型工程问题。压缩包共245个文件&#x…

作者头像 李华