Rust中#[cfg(test)]与#[cfg(feature = "test")]的区别是什么?
Rust中
#[cfg(test)]与#[cfg(feature = "test")]的差异 两者本质属于完全不同的条件编译体系,触发逻辑、适用场景没有任何关联,唯一的相似点只是名字里都带test而已。
核心机制差异
#[cfg(test)]是Rust编译器内置的条件编译标记
不需要在项目配置里做任何声明,只要执行cargo test命令编译测试目标时,rustc会自动传入--cfg test参数开启该标记。这个标记仅对当前正在测试的包生效,不会传递给依赖项,正常执行cargo build/cargo run时默认关闭,除非手动通过RUSTFLAGS强行传入该配置(常规开发不会这么做)。#[cfg(feature = "test")]是Cargo提供的自定义功能开关
它属于Cargo feature机制的一部分,必须先在项目的Cargo.toml的[features]区块明确定义名为test的feature,该标记才有可能生效。它的开启/关闭完全由手动控制:要么执行命令时加--features test参数指定,要么被其他feature/依赖项引入开启,和是否在跑测试没有任何默认绑定关系——哪怕跑cargo test,只要没手动开这个feature,对应代码就不会被编译。
注意:非常不推荐把自定义feature命名为
test,和内置cfg重名是极高概率出混淆bug的做法,测试类的辅助功能一般命名为test-utils/mock-support这类辨识度高的名字。
实际效果演示
用一个最小示例验证两者的区别:
- 首先在
Cargo.toml中声明test feature(不声明的话#[cfg(feature = "test")]永远不生效):
[package] name = "cfg-demo" version = "0.1.0" edition = "2021" [features] test = [] # 显式定义自定义test feature
- 在
src/main.rs中写入测试代码:
// 仅在内置test cfg开启时编译 #[cfg(test)] fn test_cfg_only() { println!("内置test cfg已触发,函数编译生效"); } // 仅在自定义test feature开启时编译 #[cfg(feature = "test")] fn feature_test_only() { println!("自定义test feature已触发,函数编译生效"); } #[test] fn check_cfg() { #[cfg(test)] println!("测试流程:内置test cfg 已开启"); #[cfg(feature = "test")] println!("测试流程:自定义test feature 已开启"); } fn main() { println!("main函数正常运行"); }
- 不同命令的执行结果对比:
- 执行
cargo run(普通运行):两个标记都不开启,只会输出main函数正常运行,两个条件编译的函数都不会被编入二进制。 - 执行
cargo test(普通测试,不带feature参数):仅内置testcfg自动开启,测试输出会打印测试流程:内置test cfg 已开启,自定义feature对应的代码完全不参与编译。 - 执行
cargo run --features test(普通运行+开test feature):内置test cfg不开启,仅自定义feature生效,这时候如果在main里调用feature_test_only()可以正常编译运行,调用test_cfg_only()则会报函数不存在的编译错误。 - 执行
cargo test --features test(测试+开test feature):两个标记同时生效,测试里两条日志都会打印,两个条件编译函数都存在。
适用场景区别
#[cfg(test)]:用于写单元测试专属代码,比如测试辅助函数、mock逻辑、只在测试时引入的依赖,这部分代码完全不会进入正式release构建,不会增加产物体积,是Rust单测的标准写法。#[cfg(feature = "xxx")](不管是不是命名为test):用于可选功能的按需编译,比如给库的使用者暴露测试辅助工具、可选的扩展功能、兼容不同环境的逻辑,和测试流程本身没有绑定关系,需要用户手动选择是否开启。
内容的提问来源于stack exchange,提问作者savx2
相关产品推荐
相关产品推荐

