Sway 实验性特性Experimental Features完全指南特性开关、优先级机制与条件编译【免费下载链接】sway Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/swaySway 编译器通过一套统一的实验性特性Experimental Features机制在语言特性尚未稳定、需要受控引入破坏性变更或需兼容旧行为时为开发者提供显式的 opt-in/opt-out 开关。本文基于 docs/book/src/reference/experimental_features.md 系统讲解该机制的完整脉络如何在Forc.toml、forcCLI 与环境变量三个层面配置特性开关、三者之间的覆盖优先级以及如何用#[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 #5727references默认启用true引用类型特性对应 tracking issue #5063new_hashing默认启用true新的哈希方案对应 tracking issue #7256文档中以它作为 flag 示例str_array_no_padding默认关闭false字符串数组不带填充字节的新布局对应 tracking issue #7528dynamic_storage默认关闭false动态存储特性对应 tracking issue #7560从宏定义可以看出Feature枚举与ExperimentalFeatures结构体均由这 5 个 flag 宏展开生成每个 flag 在ExperimentalFeatures中对应一个bool字段其默认值直接由宏参数决定。因此上述默认状态是写死在编译器里的如果你在Forc.toml的experimental字段中没有列出某个已存在特性 flag编译时就会使用这个默认值。三级配置方式与覆盖优先级实验性特性可以通过三种方式启用和禁用Forc.toml清单级、forcCLI 参数命令级、环境变量会话级。三者之间存在明确的优先级关系这也是整个机制中最重要的规则环境变量覆盖 CLI 参数CLI 参数覆盖Forc.toml配置。具体的解析顺序在 sway-features/src/lib.rs 的ExperimentalFeatures::new中被完整实现注释明确列出了五步应用顺序manifestForc.toml各 flag 之间无特定顺序CLI 的--no-experimentalCLI 的--experimental环境变量FORC_NO_EXPERIMENTAL环境变量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被反序列化为HashMapString, 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 ,)]声明了experimental与no_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。也就是说build、check、test、contract-id、predicate-root等命令都可以直接携带这两个 flag。随后这些参数被传入编译链路以forc build为例forc/src/ops/forc_build.rs 将cmd.experimental.experimental与cmd.experimental.no_experimental取出最终汇入 forc-pkg/src/pkg.rs 的build函数在该函数中对依赖图上的每个包调用ExperimentalFeatures::new(manifest.project.experimental, experimental, no_experimental)完成合并。方式三环境变量FORC_EXPERIMENTAL/FORC_NO_EXPERIMENTAL在环境层面使用FORC_EXPERIMENTAL与FORC_NO_EXPERIMENTAL两个环境变量分别启用和禁用特性值为逗号分隔的特性名列表。常见用法是在运行forc前先行设置# 只开启 some_feature 与 other_feature FORC_EXPERIMENTALsome_feature,other_feature forc build # 只关闭 some_feature 与 other_feature FORC_NO_EXPERIMENTALsome_feature,other_feature forc build # 同时使用两个变量分别管理开启与关闭集合 FORC_EXPERIMENTALsome_feature FORC_NO_EXPERIMENTALother_feature forc build实现上parse_from_environment_variables会先读取FORC_NO_EXPERIMENTAL按逗号切分并逐个禁用再读取FORC_EXPERIMENTAL按逗号切分并逐个启用——这保证了两个变量同时存在时FORC_EXPERIMENTAL对同一 flag 拥有最终决定权。sway-features自带的单元测试 sway-features/src/lib.rsok_parse_experimental_features就验证了这一行为先设置FORC_EXPERIMENTALnew_encoding与FORC_NO_EXPERIMENTAL空串解析后new_encoding被启用再交换两个变量的值解析后new_encoding被禁用。这也提示了一个实用技巧若需要清空某个方向的设置把对应环境变量置为空串即可。由于环境变量优先级最高它特别适合 CI 流水线无需改动仓库中的Forc.toml即可在特定 job 中全局切换特性组合。特性使用与错误处理在启用特性后代码中即可使用该特性对应的语法或语义。若代码用到了某个默认关闭且未在任何层开启的特性编译器会直接报错。Feature枚举上的error_because_is_disabled方法生成对应的CompileError::FeatureIsDisabled其错误消息定义在 sway-error/src/error.rsThis 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。对每个特性 flagsway-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_EXPERIMENTALf1,f2/FORC_NO_EXPERIMENTALf1,f2最高条件编译#[cfg(experimental_flag true/false)]编译期反映上述合并结果常见的使用场景包括新特性尝鲜在Forc.toml中把默认关闭的 flag如dynamic_storage、str_array_no_padding置为true立即体验新语法回归旧行为遇到默认开启的 flag如new_encoding、new_hashing、references引入的破坏性变更时用--no-experimental或FORC_NO_EXPERIMENTAL在单次构建/整个流水线中退回旧行为为迁移争取时间按包差异化配置利用特性集合按包合并的特性让工作区内不同包分别启用不同 flag互不干扰编写前后兼容库代码用#[cfg(experimental_flag true/false)]在同一份源码中维护新旧两套实现由使用方编译时的特性开关决定最终产物。需要注意的是实验性特性之所以实验性正因为它可能不稳定、可能引入破坏性变更。在升级 forc/Sway 编译器版本后务必重新核对所用 flag 的默认值与 tracking issue 中的变更说明必要时用forc build的报错信息FeatureIsDisabled定位缺失的特性开关。关键文件索引特性 flag 定义、默认值、合并优先级与 CLI/环境变量解析sway-features/src/lib.rsForc.toml的Project结构体含experimental字段解析forc-pkg/src/manifest/mod.rs编译计划中按包合并特性集合的调用点forc-pkg/src/pkg.rs的build函数与ExperimentalFeatures::new调用forc-pkg/src/pkg.rsforc各子命令对--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.rsFeatureIsDisabled编译错误消息定义sway-error/src/error.rse2e 测试中通过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),仅供参考