Sway 属性(Attributes)完全指南:元数据驱动智能合约的开发、测试与优化
2026/9/13 5:50:20 网站建设 项目流程

Sway 属性(Attributes)完全指南:元数据驱动智能合约的开发、测试与优化

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

Sway 语言通过属性(Attributes)机制为代码元素附加元数据,从而开启测试、存储纯度声明、合约可支付性、内联优化提示、弃用警告等编译期能力。本指南基于仓库 sway 中的官方文档 语言参考 · 属性 编写,并结合源码与可运行示例(docs/reference/src/code/language/annotations/src/main.sw),系统讲解七类内置属性:#[storage(...)]#[payable]#[test(...)]#[allow(...)]#[inline(...)]#[deprecated(...)],以及它们在合约、测试与优化场景中的完整用法。

从元数据到行为:Sway 属性是什么

属性(attribute)是一种元数据(metadatum),它附着在合约、函数、结构体、ABI 方法等代码元素之上,向编译器传达额外的语义信息。根据 语言参考,属性提供了超出普通类型与函数声明之外的附加功能。

在 sway-ast 中,属性声明的语法被定义为:

#[attribute] #[attribute_1, attribute_2] #[attribute()] #[attribute(arg)] #[attribute(arg_1, arg_2)] #[attribute(arg_1 = "value", arg_2 = true)]

其中方括号内可以包含一个或多个属性,每个属性可以携带零个或多个参数,参数可以拥有键值对形式的赋值(sway-ast/src/attribute.rs)。文档注释(//!///)在解析层面也被建模为属性声明,因此可以把注释视为属性的一个子集。

可复现的完整示例

为了让后续每个属性的讲解都能直接运行验证,先给出一个覆盖所有内置属性的完整合约示例(与 docs/reference/src/code/language/annotations/src/main.sw 一致):

contract; storage { my_storage_namespace { var: u64 = 0, } } abi MyContract { #[payable] fn deposit(); } #[storage(read)] fn read() { let variable = storage::my_storage_namespace.var.read(); } #[storage(write)] fn write() { storage::my_storage_namespace.var.write(storage::my_storage_namespace.var.read() + 1); } #[storage(read, write)] fn read_write() { let var = storage::my_storage_namespace.var.read(); storage::my_storage_namespace.var.write(var + 1); } #[allow(dead_code)] fn unused_function() {} #[test] fn equal() { assert_eq(1 + 1, 2); } #[test(should_revert)] fn unequal() { assert_eq(1 + 1, 3); } #[test(should_revert = "18446744073709486084")] fn assert_revert_code() { assert(1 + 1 == 3); } #[test(should_revert = "42")] fn custom_revert_code() { revert(42); } #[inline(never)] fn foo() {} #[inline(always)] fn bar() {} #[deprecated(note = "This is deprecated.")] struct DeprecatedStruct {} #[allow(deprecated)] fn using_deprecated_struct() { let _ = DeprecatedStruct {}; }

下文将逐一拆解每个属性。所有代码均可放入一个contract文件中,通过forc build/forc test验证。

存储纯度:#[storage(read, write)]

#[storage(...)]属性声明一个函数的纯度(purity),即该函数是否:

  • 读取存储(read)
  • 写入存储(write)
  • 既读取又写入存储(read, write)
  • 既不读也不写——即纯函数(pure)

当函数是纯函数时,不需要添加任何存储属性,属性可以省略;只要函数涉及存储访问,就必须在函数签名上方放置正确的注解(attributes/storage.md)。

三种写法的语义如下:

写法语义典型场景
#[storage(read)]只读取存储,不修改查询余额、读取配置
#[storage(write)]只写入存储,不读取覆盖状态、初始化
#[storage(read, write)]读取并写入存储读改写类逻辑,如计数器累加

示例:

#[storage(read)] fn read() { let variable = storage::my_storage_namespace.var.read(); } #[storage(write)] fn write() { storage::my_storage_namespace.var.write(storage::my_storage_namespace.var.read() + 1); } #[storage(read, write)] fn read_write() { let var = storage::my_storage_namespace.var.read(); storage::my_storage_namespace.var.write(var + 1); }

存储声明使用命名空间(namespace)语法(my_storage_namespace),配合.read()/.write()方法完成状态读写。关于存储操作的完整说明,可参考文档 common storage operations(对应路径 docs/reference/src/documentation/operations/storage/index.md)。

接受资产:#[payable]

#[payable]属性用于允许一个 合约 的 函数 接受通过调用转发(forwarded)的资产(asset)。默认情况下,如果调用方向合约函数转发了资产,函数会拒绝该调用;只有显式标注了#[payable]的函数才允许接收资产(attributes/payable.md)。

典型写法是在 ABI 方法声明处标注:

abi MyContract { #[payable] fn deposit(); }

该属性常与msg_amount()等上下文函数配合,用于实现充值、存款、购买等需要同时接收资产与更新状态的业务逻辑。需要注意的是,#[payable]只决定"能否接收资产",并不豁免其他安全检查(如重入防护、CEI 模式),这些仍需开发者自行保证。

单元测试:#[test]#[test(should_revert)]

Sway 提供#[test]属性,用于在 Sway 代码中直接编写单元测试(attributes/test.md)。

成功用例

#[test]表示一个测试,只要测试执行过程中没有 revert(回滚),即判定为通过:

#[test] fn equal() { assert_eq(1 + 1, 2); }

回滚用例

当测试的预期行为是代码应当回滚时,使用#[test(should_revert)]。此时如果测试确实发生了 revert,会被报告为通过:

#[test(should_revert)] fn unequal() { assert_eq(1 + 1, 3); }

指定回滚码

should_revert还可以携带一个具体的回滚码(revert code),用于精确匹配测试中抛出的回滚值。示例中给出了两种典型用法(docs/reference/src/code/language/annotations/src/main.sw):

#[test(should_revert = "18446744073709486084")] fn assert_revert_code() { assert(1 + 1 == 3); } #[test(should_revert = "42")] fn custom_revert_code() { revert(42); }

assert失败时 Sway 会产生一个默认回滚码;而当开发者主动调用revert(42)时,则抛出自定义回滚码42。如果测试中实际产生的回滚码与should_revert指定的值一致,测试通过。例如custom_revert_code会精确匹配revert(42)的返回值。测试通过forc test命令执行。

内联优化:#[inline(never)]#[inline(always)]

进行函数调用时,编译器既可能生成对函数定义处的调用指令,也可能将函数体代码复制(inline)到调用点,以减少额外代码生成(attributes/inline.md)。

Sway 编译器会基于内部启发式规则(internal heuristics)自动决定是否内联函数。#[inline(...)]属性用于建议(suggest)而非强制要求编译器采用某种生成策略:

  • #[inline(never)]:建议生成对函数定义处的调用代码(即不要内联)
  • #[inline(always)]:建议将函数体复制到调用点(即尽量内联)
#[inline(never)] fn foo() {} #[inline(always)] fn bar() {}

由于它只是"建议",编译器最终仍可能根据启发式规则做出不同决策;因此该属性适合作为性能调优的提示,而不是硬性保证。在循环热路径、递归函数、体积与速度权衡等场景中,开发者可以通过这两个关键字向编译器表达意图。

抑制警告:#[allow(...)]

#[allow(...)]属性用于关闭编译器对特定代码情况发出的警告(attributes/allow.md)。

#[allow(dead_code)]

关闭针对未使用(unused)代码的警告:

#[allow(dead_code)] fn unused_function() {}

当项目处于开发初期、函数尚未被调用时,该属性可以避免编译器对未使用代码发出警告,同时保留函数定义。

#[allow(deprecated)]

关闭针对使用已弃用(deprecated)项时的警告:

#[allow(deprecated)] fn using_deprecated_struct() { let _ = DeprecatedStruct {}; }

当代码中不可避免地需要引用一个已被标记弃用的项时(例如迁移期间仍需兼容旧 API),使用该属性可以显式声明"我知道这是弃用的",从而让编译输出保持干净。需要注意的是,#[allow(...)]只是静默警告,并不会改变被允许项的实际语义。

标记弃用:#[deprecated(note = "...")]

#[deprecated]属性将某个项标记为弃用,使得编译器在该项被使用的每个位置发出警告;该警告可以通过#[allow(deprecated)]关闭(attributes/deprecated.md)。

此外,可以通过note参数自定义警告消息:

#[deprecated(note = "This is deprecated.")] struct DeprecatedStruct {}

这样,任何使用DeprecatedStruct的代码都会收到带有This is deprecated.提示的警告,帮助团队在升级过程中向调用方解释替代方案。弃用警告属于编译期提示,不影响编译产物。

属性如何进入编译器:AST 层解析

从实现角度看,属性在 sway-ast/src/attribute.rs 中被解析为AttributeDecl,它由AttributeHashKind(标识是外层#[...]还是内层#![...])和方括号包裹的、以逗号分隔的属性列表组成。每个属性(Attribute)可以携带括号包裹的参数列表(AttributeArg),参数可以是"仅名称"或"名称 = 值"两种形式。这套 AST 结构正好对应了上文各种写法的统一来源:

#[attribute(arg_1 = "value", arg_2 = true)]

也就是说,无论#[storage(read, write)]#[test(should_revert = "42")]还是#[deprecated(note = "...")],在语法层面都是同一种"属性 + 参数(可含键值对)"的通用结构,语义差异完全由属性名与参数内容决定。这解释了为什么 Sway 可以保持如此简洁的元数据语法。

总结:何时使用哪个属性

属性作用对象核心作用
#[storage(read)]/#[storage(write)]/#[storage(read, write)]函数声明存储读写纯度,读取/写入存储时必需
#[payable]合约 ABI 函数允许函数接受调用转发的资产
#[test]函数声明单元测试(不 revert 即通过)
#[test(should_revert)]函数声明预期回滚的测试(revert 即通过)
#[test(should_revert = "code")]函数声明回滚测试并精确匹配回滚码
#[inline(never)]/#[inline(always)]函数建议编译器不内联 / 内联(仅供参考)
#[allow(dead_code)]任意项关闭未使用代码警告
#[allow(deprecated)]任意项关闭使用弃用项的警告
#[deprecated(note = "...")]任意项标记弃用,使用处产生自定义提示警告

完整可运行的示例位于 docs/reference/src/code/language/annotations/src/main.sw,AST 级语法解析实现位于 sway-ast/src/attribute.rs。掌握这七类属性,即可在合约开发中精确控制存储访问声明、资产接收权限、单元测试策略、代码生成优化与 API 迁移流程。

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

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询