rustc 的 --check-cfg 如何检查条件编译:为 Cargo 项目声明期望的 cfg 名称与取值
【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust
#[cfg]条件写错了名字或取值——比如把windows拼成widnows、用了一个不存在的 feature——编译不会报错,那段代码只是静默地不再参与编译。rustc的--check-cfg就是用来堵住这类问题的:你用 check cfg 声明式在编译前声明"哪些 cfg 名称和取值是预期的",编译时所有可达的#[cfg]都会对照这份期望清单,不匹配的名称或取值会触发unexpected_cfgslint,默认级别为Warn。
本文基于 Checking conditional configurations 与 Cargo Specifics 两篇 rustc 文档,讲清楚如何在 rustc 命令行直接声明期望,以及 Cargo 项目(Cargo.toml、build.rs)中的三种对应机制。
--check-cfg 的声明语法
基本形式:
rustc --check-cfg 'cfg(name, values("value1", "value2", ... "valueN"))'其中name是裸标识符(不带引号),values()里每一项是带引号的字符串字面量。name对应条件名称(如feature、my_cfg),每个"value"是该条件允许的取值之一。
几种常用变体:
期望"无值"条件(即
#[cfg(foo)]这种写法):在values()内使用none()谓词,它可以和任意数量的"value"混排:rustc --check-cfg 'cfg(name)' rustc --check-cfg 'cfg(name, values(none()))'只检查名称、不管取值,有两种相反语义:
# 无期望值:name 的每个取值都会触发 lint rustc --check-cfg 'cfg(name, values())' # 任意值都接受:name 的取值永不触发 lint rustc --check-cfg 'cfg(name, values(any()))'多个名称共享同一取值集合时,把名称并列写在一条声明里,避免重复:
rustc --check-cfg 'cfg(name1, ..., nameN, values("value1", "value2", ... "valueN"))'不指定任何名称和取值:
rustc --check-cfg 'cfg()'表示启用检查,且没有任何用户自定义期望。
几点行为边界,来自文档的明确说明:
- 使用
--cfg激活某个配置不会隐式加入期望清单;所有期望名称和取值都必须通过 check cfg 声明式显式给出。 - 同一条件的
--check-cfg参数可以重复出现,重复时各取值集合会合并,values(any())优先。 - 命令行上已经激活的
--cfg参数本身目前不被检查,文档注明未来可能改变。 - 检查范围:
rustc承诺至少检查所有可达的(reachable)#[cfg];不可达的#[cfg]当前不检查,但未来可能开始检查且不算破坏性变更。
哪些写法会被检查、诊断长什么样
只要指定了cfg(...)选项,以下四种写法都会逐一对照期望清单:
#[cfg(name = "value")]属性#[cfg_attr(name = "value")]属性#[link(name = "a", cfg(name = "value"))]属性cfg!(name = "value")宏调用
当条件中出现不在期望清单里的取值(或名称)时,编译器报告unexpected_cfgslint 诊断。下面是文档给出的诊断示例(示例结果):
warning: unexpected `cfg` condition name: `widnows` --> lint_example.rs:1:7 | 1 | #[cfg(widnows)] | ^^^^^^^ | = note: `#[warn(unexpected_cfgs)]` on by default也就是说,验证方式是观察编译输出中是否出现unexpected_cfgs警告:拼错的条件名、未声明的取值都会逐条列出文件位置;编译通过且无此警告,说明代码里用到的#[cfg]条件都落在声明的期望范围内。
文档示例:带 feature 的完整命令
文档给出的一条可参考的命令(example.rs为示意文件名,替换为你要检查的文件):
rustc --check-cfg 'cfg(feature, values("lion", "zebra"))' \ --cfg 'feature="lion"' example.rs这条命令行表示:crate 有两个 featurelion和zebra,当前启用的是lion。对应源码中的检查结果:
#[cfg(feature = "lion")] // 预期内:"lion" 在期望取值列表中 fn tame_lion(lion: Lion) {} #[cfg(feature = "zebra")] // 预期内:"zebra" 是期望取值,只是本次未激活,条件求值为 false fn ride_zebra(z: Zebra) {} #[cfg(feature = "platypus")] // 预期外:"platypus" 不在期望取值中,触发 unexpected_cfgs fn poke_platypus() {} #[cfg(feechure = "lion")] // 预期外:"feechure" 不是已声明的条件名称,触发 unexpected_cfgs fn tame_lion() {} #[cfg(windows = "unix")] // 预期外:well known 的 'windows' cfg 不接受任何值,触发 unexpected_cfgs fn tame_windows() {}这个例子顺带展示了--check-cfg与--cfg的分工:--cfg负责"激活"某个条件(决定条件是否为真),--check-cfg负责"声明预期"(决定条件是否合法)。
Well known names:一批名称无需手动声明
rustc维护了一份 well-known 名称及其取值的内置清单,只要至少传入一个--check-cfg参数,这些名称就会隐式加入期望。文档给出的清单(截至 2026-08-11)包括:
clippy、debug_assertions、doc、doctest、fmt_debug、miri、overflow_checks、panic、proc_macro、relocation_model、rust_analyzer、rustfmt、sanitize、sanitizer_cfi_generalize_pointers、sanitizer_cfi_normalize_integers、target_abi、target_arch、target_endian、target_env、target_family、target_object_format、target_feature、target_has_atomic、target_has_atomic_primitive_alignment、target_has_atomic_load_store、target_os、target_pointer_width、target_thread_local、target_vendor、ub_checks、contract_checks、unix、windows。
因此#[cfg(target_arch = "x86_64")]这类目标相关条件不需要你逐个声明,但要注意:内置的windows/unix这类名称本身不带值,写成#[cfg(windows = "unix")]仍然会触发 lint。与values(any())类似,也可以给--check-cfg传入cfg(any())来关闭对 well-known 名称的检查。
另有一条版本相关的说明:从 1.85.0 起,testcfg 被归为"userspace"配置——尽管它同样由rustc设置,但文档认为它应当由构建系统自行管理(即由构建系统在--check-cfg中声明)。
Cargo 项目中的三种声明方式
Cargo 生态下,期望清单有三个声明位置,按配置来源的确定程度选择:
方式一:feature 由 Cargo 自动声明
Cargo.toml的[features]表中每个 feature,Cargo 都会自动把对应 cfg 声明为期望:
[features] serde = ["dep:serde"] my_feature = []所以对 feature 条件,通常不需要再手动写--check-cfg,只需检查代码中使用的 feature 名是否真的存在于[features]表。
方式二:Cargo.toml的[lints.rust]表(静态已知的自定义 cfg)
当自定义配置是静态已知、不依赖 build script 时,可以在[lints.rust]下用unexpected_cfgs的check-cfg项写入自定义静态--check-cfg参数:
[lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(has_foo)'] }方式三:build.rs输出cargo::rustc-check-cfg(动态决定的自定义 cfg)
当自定义配置由 build script 动态决定、通过cargo::rustc-cfg激活时,对应的期望要用cargo::rustc-check-cfg指令声明(Cargo 1.80 新增):
fn main() { println!("cargo::rustc-check-cfg=cfg(has_foo)"); // ^^^^^^^^^^^^^^^^^^^^^^ new with Cargo 1.80 if has_foo() { println!("cargo::rustc-cfg=has_foo"); } }排查与限制
激活了但没声明:
--cfg只激活条件、不产生期望。代码里用到#[cfg(my_cfg)]时,必须另有--check-cfg 'cfg(my_cfg)'(或 Cargo 侧对应机制)声明,否则触发 lint。--cfg与--check-cfg的对应关系:文档给出了等价对照表,转换时按此表核对:--cfg对应的 --check-cfg无 无,或 --check-cfg=cfg()(仅启用检查)--cfg foo--check-cfg=cfg(foo)或--check-cfg=cfg(foo, values(none()))--cfg foo=""--check-cfg=cfg(foo, values(""))--cfg foo="bar"--check-cfg=cfg(foo, values("bar"))--cfg foo="1" --cfg foo="2"--check-cfg=cfg(foo, values("1", "2"))--cfg foo="1" --cfg bar="2"--check-cfg=cfg(foo, values("1")) --check-cfg=cfg(bar, values("2"))--cfg foo --cfg foo="bar"--check-cfg=cfg(foo, values(none(), "bar"))检查范围是"可达"的
#[cfg]:不可达条件当前不检查,但文档明确保留未来开始检查的可能,声明期望时不要依赖这个豁免。nightly 辅助手段:
--print=check-cfg(配合-Zunstable-options,见 print-check-cfg)可以把编译器当前使用的配置输出为可复用的--check-cfg参数,适合用来核对实际生效的配置;这是 nightly 不稳定选项,不适用于稳定版构建流程。
配置完成后跑一次编译,编译输出里不再出现unexpected_cfgs警告、且代码中每个可达的#[cfg]名称与取值都在声明清单内,即说明期望声明与实际使用一致;新增 feature 或自定义 cfg 时,记得同步更新[features]表、[lints.rust]或 build script 中的声明。
【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考