What is a clean way of having examples that depend on optional features not be compiled during a cargo test invocation?
For context: I have a crate with a non-default feature that enables some optional functionality. For this crate, I also have some examples in the examples/ directory, some of which demonstrate use of said optional functionality, but cargo test will automatically compile all examples to help prevent bitrot, including the ones that use the optional functionality. Those examples will fail to compile, obviously causing cargo test to report failures that are simply due to features not being specified.
With integration tests, I typically annotate them with #![cfg(feature = "featuregate")] as the first non-comment line of the file; since they generally use the test driver, an empty file is valid. If I apply this to an example that has a main() function, the then compiler rightfully sees the file as empty and complains about a lack of main(). I know I could wrap everything in an internal module and conditionally-compile the module, but that a) is ugly, b) complicates the examples unnecessarily, and c) feels like it's using a scalpel to solve a problem more suited for a sledgehammer.
(I'm aware of the required-features manifest key, but this does not prevent compilation when the feature is not enabled, merely making the compilation errors more useful.)
Any commentary or solutions would be welcome!
#[cfg(feature = "featuregate")]
fn main() {
println!("the real example using the feature");
}
#[cfg(not(feature = "featuregate"))]
fn main() {
println!("this example is not available, enable featuregate first")
}
what do you mean? if the feature is not enabled, the example will NOT be built by cargo, including cargo test.
# Cargo.toml
[package]
name = "mylib"
version = "0.1.0"
edition = "2024"
[features]
default = []
foo = []
[[example]]
name = "foo-example"
required-features = ["foo"]
// lib.rs
#[cfg(feature = "foo")]
pub fn foo() {
println!("optional feature foo");
}
// examples/foo-example.rs
fn main() {
mylib::foo();
}
this is the output with required-features:
$ cargo test
Compiling mylib v0.1.0 (/tmp/playground-1779328687595)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.09s
Running unittests src/lib.rs (target/debug/deps/mylib-20e286f7fbfd44cc)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/main.rs (target/debug/deps/mylib-ee0f20625bf45e76)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests mylib
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
and this is the output without required-features:
$ cargo test
Compiling mylib v0.1.0 (/tmp/playground-1779328687595)
error[E0425]: cannot find function `foo` in crate `mylib`
--> examples/foo-example.rs:2:9
|
2 | mylib::foo();
| ^^^ not found in `mylib`
|
note: found an item that was configured out
--> src/lib.rs:3:8
|
2 | #[cfg(feature = "foo")]
| --------------- the item is gated behind the `foo` feature
3 | pub fn foo() {
| ^^^
For more information about this error, try `rustc --explain E0425`.
error: could not compile `mylib` (example "foo-example") due to 1 previous error
Huh. I definitely tested that; I must have misspelled required-features or something along those lines. Well, thank you! I feel silly now 