news 2026/9/12 12:46:08

Sway 实验性特性(Experimental Features)完全指南:特性开关、优先级机制与条件编译

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Sway 实验性特性(Experimental Features)完全指南:特性开关、优先级机制与条件编译

Sway 实验性特性(Experimental Features)完全指南:特性开关、优先级机制与条件编译

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

Sway 编译器通过一套统一的"实验性特性(Experimental Features)"机制,在语言特性尚未稳定、需要受控引入破坏性变更或需兼容旧行为时,为开发者提供显式的 opt-in/opt-out 开关。本文基于 docs/book/src/reference/experimental_features.md 系统讲解该机制的完整脉络:如何在Forc.tomlforcCLI 与环境变量三个层面配置特性开关、三者之间的覆盖优先级,以及如何用#[cfg(experimental_<flag> = true/false)]编写依赖特性的条件编译代码。读完本文,你将能够准确识别当前仓库已内置的全部特性 flag,并在自己的 Sway 工程中按需、可控地启用或停用这些语言特性。

实验性特性是什么,为什么要用它

Sway 编译器支持实验性特性,其主要用途有三类(对应仓库中标注了tracking-issue标签的 GitHub Issue):

  • 开发尚未稳定的大型语言特性:例如 References(引用)特性,在开发过程中可能不稳定,需要逐步打磨后才正式放开;
  • 以受控方式引入破坏性变更:例如 Partial equivalence(部分等价性)相关改造,允许使用方显式选择迁移时机,而不是在升级编译器时被动接受破坏;
  • 在不兼容变更出现时保留旧编译器行为:例如 New Hashing(新哈希方案),让需要旧行为的工程可以在升级后继续按原语义编译。

每项实验性特性在 Sway 仓库中都有一个对应的tracking-issue,其中包含该特性的详细描述以及它引入的所有破坏性变更,可作为启用前的必读资料。当前已激活与已合入的特性清单均可通过该标签在仓库 Issue 列表中检索。

当前仓库内置的特性 flag 全集

特性 flag 的权威定义位于 sway-features/src/lib.rs,通过features!宏一次性声明。截至当前仓库版本,共定义了 5 个实验性特性,每个特性同时携带其默认开关状态(true表示默认启用,false表示默认关闭)和对应的 tracking issue:

特性 flag默认状态特性说明
new_encoding默认启用(true新的 ABI/数据编码方案(对应 tracking issue #5727)
references默认启用(true引用类型特性(对应 tracking issue #5063)
new_hashing默认启用(true新的哈希方案(对应 tracking issue #7256),文档中以它作为 flag 示例
str_array_no_padding默认关闭(false字符串数组不带填充字节的新布局(对应 tracking issue #7528)
dynamic_storage默认关闭(false动态存储特性(对应 tracking issue #7560)

从宏定义可以看出,Feature枚举与ExperimentalFeatures结构体均由这 5 个 flag 宏展开生成,每个 flag 在ExperimentalFeatures中对应一个bool字段,其默认值直接由宏参数决定。因此上述默认状态是写死在编译器里的;如果你在Forc.tomlexperimental字段中没有列出某个已存在特性 flag,编译时就会使用这个默认值。

三级配置方式与覆盖优先级

实验性特性可以通过三种方式启用和禁用:Forc.toml(清单级)、forcCLI 参数(命令级)、环境变量(会话级)。三者之间存在明确的优先级关系,这也是整个机制中最重要的规则:

环境变量覆盖 CLI 参数,CLI 参数覆盖Forc.toml配置。

具体的解析顺序在 sway-features/src/lib.rs 的ExperimentalFeatures::new中被完整实现,注释明确列出了五步应用顺序:

  1. manifest(Forc.toml,各 flag 之间无特定顺序);
  2. CLI 的--no-experimental
  3. CLI 的--experimental
  4. 环境变量FORC_NO_EXPERIMENTAL
  5. 环境变量FORC_EXPERIMENTAL

也就是说,即便某个特性在Forc.toml中已开启,你依然可以用 CLI 或环境变量在单次构建中临时关掉它;反之,Forc.toml中未开启的特性也可以由 CLI/环境变量临时打开,无需改动清单文件。ExperimentalFeatures::new先从default()出发,依次叠加清单、CLI 禁用以外的 CLI 启用项,最后再解析两个环境变量完成覆盖,最终得到一个合并后的特性集合传给编译过程。

下面分别介绍三种配置方式的具体写法。

方式一:Forc.toml中的[project] experimental字段

要为某个包启用/禁用实验性特性,在Forc.toml[project]小节内使用experimental字段即可。它是一个键值映射:键为特性 flag 名,值为true(启用)或false(禁用):

[project] name = "my_contract" version = "0.1.0" license = "Apache-2.0" # ... 其他项目字段 ... experimental = { new_hashing = true, str_array_no_padding = false }
  • 若某个已存在特性未出现在experimental字段中,则使用上文表格中的编译期默认值;
  • 若填写的 flag 名不存在,会得到Unknown experimental feature: "<flag>".之类的解析错误(详见下文"错误处理"小节)。

从实现看,forc-pkg/src/manifest/mod.rs 的Project结构体中,experimental被反序列化为HashMap<String, bool>,并由parse_from_package_manifest逐项通过set_enabled_by_name写入特性集合;set_enabled_by_name内部会对 flag 名做trim()并匹配已知名称,空串被静默忽略,未知名称则报错。

仓库的 e2e 测试大量使用了这一配置方式,例如 test/src/e2e_vm_tests/test_programs/should_pass/language/associated_const_in_decls_of_other_constants/test.dynamic_storage.toml 中:

experimental = { new_encoding = true, dynamic_storage = true }

test.dynamic_storage.toml这类后缀配置由 e2e 测试框架按需叠加到被测包上,用于在特定用例中开启对应特性——这恰好验证了"在Forc.toml层开启特性"这一路径的真实运作方式。

方式二:forcCLI 的--experimental/--no-experimental

在命令行层面,使用两个编译期 flag 完成 opt-in 与 opt-out:--experimental用于开启、--no-experimental用于关闭:

forc build --experimental some_feature --no-experimental some_other_feature

同时操作多个特性时用逗号分隔(注意中间不要有空格,逗号即分隔符):

forc build --experimental some_feature_1,some_feature_2 --no-experimental some_other_feature_1,some_other_feature_2

这两个 flag 可以同时出现:--no-experimental的优先级高于清单配置、低于--experimental(见前文顺序),所以同一命令行里既开启又关闭不同特性是完全合法的组合。

从实现看,sway-features/src/lib.rs 中的CliFields用 clap 的#[clap(long, value_delimiter = ',')]声明了experimentalno_experimental两个参数,value_delimiter = ','正是命令行逗号分隔语义的来源。该CliFields被挂载到多个 forc 子命令上,例如 forc/src/cli/commands/build.rs、forc/src/cli/commands/check.rs、forc/src/cli/commands/test.rs、forc/src/cli/commands/contract_id.rs、forc/src/cli/commands/predicate_root.rs。也就是说,buildchecktestcontract-idpredicate-root等命令都可以直接携带这两个 flag。

随后这些参数被传入编译链路:以forc build为例,forc/src/ops/forc_build.rs 将cmd.experimental.experimentalcmd.experimental.no_experimental取出,最终汇入 forc-pkg/src/pkg.rs 的build函数,在该函数中对依赖图上的每个包调用ExperimentalFeatures::new(&manifest.project.experimental, experimental, no_experimental)完成合并。

方式三:环境变量FORC_EXPERIMENTAL/FORC_NO_EXPERIMENTAL

在环境层面,使用FORC_EXPERIMENTALFORC_NO_EXPERIMENTAL两个环境变量分别启用和禁用特性,值为逗号分隔的特性名列表。常见用法是在运行forc前先行设置:

# 只开启 some_feature 与 other_feature FORC_EXPERIMENTAL=some_feature,other_feature forc build # 只关闭 some_feature 与 other_feature FORC_NO_EXPERIMENTAL=some_feature,other_feature forc build # 同时使用两个变量,分别管理开启与关闭集合 FORC_EXPERIMENTAL=some_feature FORC_NO_EXPERIMENTAL=other_feature forc build

实现上,parse_from_environment_variables会先读取FORC_NO_EXPERIMENTAL(按逗号切分并逐个禁用),再读取FORC_EXPERIMENTAL(按逗号切分并逐个启用)——这保证了两个变量同时存在时FORC_EXPERIMENTAL对同一 flag 拥有最终决定权。sway-features自带的单元测试 sway-features/src/lib.rs(ok_parse_experimental_features)就验证了这一行为:先设置FORC_EXPERIMENTAL=new_encodingFORC_NO_EXPERIMENTAL=(空串),解析后new_encoding被启用;再交换两个变量的值,解析后new_encoding被禁用。这也提示了一个实用技巧:若需要"清空"某个方向的设置,把对应环境变量置为空串即可。

由于环境变量优先级最高,它特别适合 CI 流水线:无需改动仓库中的Forc.toml,即可在特定 job 中全局切换特性组合。

特性使用与错误处理

在启用特性后,代码中即可使用该特性对应的语法或语义。若代码用到了某个默认关闭且未在任何层开启的特性,编译器会直接报错。Feature枚举上的error_because_is_disabled方法生成对应的CompileError::FeatureIsDisabled,其错误消息定义在 sway-error/src/error.rs:

This needs "{feature}" to be enabled, but it is currently disabled. For more details go to {url}.

即错误信息会明确指出缺少哪个特性 flag,并附带该特性的 tracking issue 链接,便于你决定是开启特性、还是改用其他实现方式。

另外需要注意的是:实验性特性的合并结果是以"包"为单位计算的。forc-pkg/src/pkg.rs 的build在遍历编译计划中的每个包时,都会用该包自己的清单manifest.project.experimental与全局 CLI/环境变量合并出一份特性集合,因此工作区内不同包可以各自拥有不同的特性组合。

条件编译:#[cfg(experimental_<flag> = true/false)]

实验性特性还深度集成在 Sway 的条件编译机制中。Sway 的#[cfg(...)]属性支持三类条件(详见 docs/book/src/reference/attributes.md 中的 Cfg 一节):

  • #[cfg(target = "<target>")]:目标平台,取"evm""fuel"
  • #[cfg(program_type = "<program_type>")]:程序类型,取"predicate""script""contract""library"
  • #[cfg(experimental_<feature_flag> = true/false)]:实验性特性开关状态,其中<feature_flag>必须是已知的特性 flag。

对每个特性 flag,sway-features都会自动生成一个名为experimental_<feature_flag>的布尔型 cfg 参数(这正是Feature::CFG常量的内容)。被#[cfg]标注的代码元素仅在以下两种情况下参与编译:

  • 编译时该特性已启用,且experimental_<feature_flag>写为true
  • 编译时该特性未启用,且experimental_<feature_flag>写为false

结合当前仓库的实际 flag,一个可运行的真实示例是:

// 仅当 new_hashing 特性启用时编译 #[cfg(experimental_new_hashing = true)] fn uses_new_hashing() { log("Compiled only when `new_hashing` is enabled."); } // 仅当 new_hashing 特性禁用时编译 #[cfg(experimental_new_hashing = false)] fn uses_new_hashing() { log("Compiled only when `new_hashing` is disabled."); }

当条件依赖多个特性时,可以叠加多个#[cfg]属性——所有条件同时满足才会编译该代码元素:

#[cfg(experimental_some_feature = true)] fn conditionally_compiled() { log("This is compiled only if `some_feature` is enabled."); } #[cfg(experimental_some_feature = false)] fn conditionally_compiled() { log("This is compiled only if `some_feature` is disabled."); } #[cfg(experimental_some_feature = true)] #[cfg(experimental_some_other_feature = true)] fn conditionally_compiled() { log("This is compiled only if both `some_feature` and `some_other_feature` are enabled."); }

上述三个同名函数利用#[cfg]实现了互斥与联合两种组合:前两个函数一真一假互斥编译,最后一个函数要求两个特性同时开启。#[cfg]判断发生在解析阶段,保证被排除的代码不会进入后续的类型检查与代码生成,因此未启用特性对应的#[cfg(false)]分支即使引用了尚未稳定的语法,也不会报错——这正是用条件编译为特性编写渐进式兼容代码的基础。

从实现看,条件编译判定逻辑位于 sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs:当遇到experimental_*形式的 cfg 参数时,会调用ExperimentalFeatures::is_enabled_for_cfg查询该特性当前的启用状态(该方法会自动在特性名前面补上experimental_前缀,并处理未知名称),再与 cfg 参数中写明的布尔值比对,不一致即判定该代码元素不参与编译。同时,sway-core/src/transform/attribute.rs 通过Feature::CFG.contains(&self.name.as_str())识别哪些 cfg 参数名属于实验性特性,并为编译器补全所有已知的experimental_*参数列表。

优先级全景与典型使用场景

综合以上内容,把三级配置与条件编译串起来看,整个实验性特性机制可归纳如下:

配置层语法优先级
Forc.toml[project] experimentalexperimental = { flag = true/false, ... }最低
forcCLI--experimental f1,f2/--no-experimental f1,f2中间
环境变量FORC_EXPERIMENTAL=f1,f2/FORC_NO_EXPERIMENTAL=f1,f2最高
条件编译#[cfg(experimental_<flag> = true/false)]编译期反映上述合并结果

常见的使用场景包括:

  1. 新特性尝鲜:在Forc.toml中把默认关闭的 flag(如dynamic_storagestr_array_no_padding)置为true,立即体验新语法;
  2. 回归旧行为:遇到默认开启的 flag(如new_encodingnew_hashingreferences)引入的破坏性变更时,用--no-experimentalFORC_NO_EXPERIMENTAL在单次构建/整个流水线中退回旧行为,为迁移争取时间;
  3. 按包差异化配置:利用"特性集合按包合并"的特性,让工作区内不同包分别启用不同 flag,互不干扰;
  4. 编写前后兼容库代码:用#[cfg(experimental_<flag> = true/false)]在同一份源码中维护新旧两套实现,由使用方编译时的特性开关决定最终产物。

需要注意的是,实验性特性之所以"实验性",正因为它可能不稳定、可能引入破坏性变更。在升级 forc/Sway 编译器版本后,务必重新核对所用 flag 的默认值与 tracking issue 中的变更说明,必要时用forc build的报错信息(FeatureIsDisabled)定位缺失的特性开关。

关键文件索引

  • 特性 flag 定义、默认值、合并优先级与 CLI/环境变量解析:sway-features/src/lib.rs
  • Forc.tomlProject结构体(含experimental字段解析):forc-pkg/src/manifest/mod.rs
  • 编译计划中按包合并特性集合的调用点:forc-pkg/src/pkg.rsbuild函数与ExperimentalFeatures::new调用(forc-pkg/src/pkg.rs)
  • forc各子命令对--experimental/--no-experimental的接入:forc/src/cli/commands/build.rs、forc/src/cli/commands/check.rs、forc/src/cli/commands/test.rs
  • #[cfg]条件编译的判定实现:sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs
  • FeatureIsDisabled编译错误消息定义:sway-error/src/error.rs
  • e2e 测试中通过Forc.toml开启特性的真实示例:test/src/e2e_vm_tests/test_programs/should_pass/language/associated_const_in_decls_of_other_constants/test.dynamic_storage.toml

【免费下载链接】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 12:45:36

Firecracker 如何配置 huge_pages 用大页支撑 microVM 内存

Firecracker 如何配置 huge_pages 用大页支撑 microVM 内存 【免费下载链接】firecracker Secure and fast microVMs for serverless computing. 项目地址: https://gitcode.com/GitHub_Trending/fi/firecracker 当你希望 microVM 的 guest 内存使用大页&#xff08;tra…

作者头像 李华
网站建设 2026/9/12 12:40:38

QSurfaceFormat完全指南:OpenGL上下文创建的隐形关键与配置避坑

前阵子帮同事排查一个Qt程序的崩溃问题&#xff1a;同一套OpenGL代码&#xff0c;在Windows上稳定运行&#xff0c;拷到一台老工作站上启动就闪退&#xff0c;报错信息指向QOpenGLContext创建失败。代码一行没改&#xff0c;GPU也支持OpenGL&#xff0c;最后定位到根因竟然是QS…

作者头像 李华
网站建设 2026/9/12 12:40:15

EMD信号去噪实战:MATLAB实现与IMF筛选策略

简介&#xff1a;面向需要在MATLAB中对一维信号进行去噪的开发者与研究人员&#xff0c;这里提供基于经验模态分解&#xff08;EMD&#xff09;的完整示例代码。资源压缩包共2个文件、均为m脚本&#xff0c;体积仅6KB&#xff0c;包含一个核心去噪函数和一个可直接运行的演示脚…

作者头像 李华
网站建设 2026/9/12 12:39:37

openpi:一条命令完成 JAX 转 PyTorch,pi0 checkpoint 导出 safetensors

openpi&#xff1a;一条命令完成 JAX 转 PyTorch&#xff0c;pi0 checkpoint 导出 safetensors 【免费下载链接】openpi 项目地址: https://gitcode.com/GitHub_Trending/op/openpi 场景切入 openpi 的 JAX 转 PyTorch 模型转换脚本就是为这类现场准备的&#xff1a;仿…

作者头像 李华
网站建设 2026/9/12 12:39:00

Spring Boot企业产供销系统开发实践与架构设计

1. 项目概述与核心需求企业产供销全流程管理系统是针对制造业企业核心业务流程设计的综合性信息化解决方案。作为一名长期从事Java企业级开发的工程师&#xff0c;我理解这类系统的核心价值在于打通传统企业中割裂的生产、供应、销售环节&#xff0c;实现数据流、物流、资金流的…

作者头像 李华