如何基于Cargo特性标志有条件地执行模块级doctest?
处理Rust文档测试中特性依赖示例的条件运行需求
我最近在给一个带Cargo特性标志的Rust模块写文档时,遇到了这么个需求:希望所有特性相关的用法示例都能一直展示在文档里,让 crate 的使用者能清楚知道有这些功能可选,但只有当对应的特性启用时,这些示例才会被当作文档测试运行,没启用特性时就跳过,避免编译报错。
先给大家看看我最开始的代码实现:
初始代码
lib.rs
//! This crate has common utility functions //! //! ``` //! assert_eq!(2, featureful::add_one(1)); //! ``` //! //! You may also want to use the feature flag `solve_halting_problem`: //! //! ``` //! assert!(featureful::is_p_equal_to_np()); //! ``` pub fn add_one(a: i32) -> i32 { a + 1 } #[cfg(feature = "solve_halting_problem")] pub fn is_p_equal_to_np() -> bool { true }
Cargo.toml
[package] name = "featureful" version = "0.1.0" authors = ["An Devloper <an.devloper@example.com>"] [features] solve_halting_problem = [] [dependencies]
遇到的问题
当我启用特性运行测试时,一切都正常,两个文档测试都能通过:
$ cargo test --features=solve_halting_problem Doc-tests featureful running 2 tests test src/lib.rs - (line 7) ... ok test src/lib.rs - (line 3) ... ok test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
但如果不启用特性直接跑测试,特性相关的那个示例就会编译失败:
$ cargo test Doc-tests featureful running 2 tests test src/lib.rs - (line 7) ... FAILED test src/lib.rs - (line 3) ... ok failures: ---- src/lib.rs - (line 7) stdout ---- error[E0425]: cannot find function `is_p_equal_to_np` in module `featureful` --> src/lib.rs:8:21 | 4 | assert!(featureful::is_p_equal_to_np()); | ^^^^^^^^^^^^^^^^ not found in `featureful`
我试过用ignore或者no_run修饰符,但这俩不管特性开没开都会生效——要么永远不运行这个测试,要么只展示代码不执行,完全达不到我想要的“特性启用时跑,禁用时不跑但仍展示”的效果。
完美解决方案
后来我发现,Rust的文档测试支持在代码块上添加条件编译属性,只要给特性相关的代码块加上cfg(feature = "xxx")的标识,就能实现我们的需求。
修改后的lib.rs代码如下:
//! This crate has common utility functions //! //! ``` //! assert_eq!(2, featureful::add_one(1)); //! ``` //! //! You may also want to use the feature flag `solve_halting_problem`: //! //! ```rust,cfg(feature = "solve_halting_problem") //! assert!(featureful::is_p_equal_to_np()); //! ``` pub fn add_one(a: i32) -> i32 { a + 1 } #[cfg(feature = "solve_halting_problem")] pub fn is_p_equal_to_np() -> bool { true }
原理解释
在文档代码块的语言标记(这里是rust)后面加上,cfg(feature = "solve_halting_problem"),Cargo就会根据当前是否启用该特性来决定如何处理这个文档测试:
- 当启用
solve_halting_problem特性时,这个代码块会被当作正常的文档测试编译并执行; - 当未启用特性时,Cargo会自动过滤掉这个测试,不会尝试编译它,但文档里依然会完整展示这段代码,完全不影响用户查看特性对应的用法。
验证效果
现在再测试就完全符合预期了:
- 不启用特性时,只会运行第一个通用的文档测试:
$ cargo test Doc-tests featureful running 1 test test src/lib.rs - (line 3) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out
- 启用特性时,两个测试都会正常运行,和之前的成功结果一致。
之前我也看到过类似的问题讨论,但大多聚焦于条件编译的函数本身,而非模块文档里的示例处理,这个方法刚好能解决我们的特定需求。
内容的提问来源于stack exchange,提问作者Shepmaster
相关产品推荐
相关产品推荐

