Cfg!() macro for features?

Hi all,

so when i learned about Cargo features and that they even work in enums i thought i could use that to select features in my program.
I also thought that the cfg!() macro would work as well, but apparently it does not in this case:
See Rust Playground

I find this example working when i remove the "#[cfg(feature = "testfeature")]" from the enum.
But when i add this line (and since the playpen doesn't enable this feature, the enum will be missing SomeFeature), the compilation fails, because SomeFeature is not available. Of course my hope was that the macro would remove the block containing SomeFeature..

Is this just something that is not supposed to work and my misunderstanding or some edge case that may be a future feature?

The cfg! macro returns a bool, it doesn't add/remove any code.

You can use the #[cfg(..)] syntax extension to achieve what you want:

fn main() {
    match () {
        #[cfg(not(feature = "testfeature"))]
        () => println!("Hello, feature-less world! {:?}", SomeEnum::SomeStuff),
        #[cfg(feature = "testfeature")]
        () => println!("Hello, feature world! {:?}", SomeEnum::SomeFeature),
    }
}

Playpen

3 Likes

Seems like you messed up #[cfg] and #[feature] attributes and cfg!() macro.

The #[cfg] attribute is for conditional compilation, when you put it above some block/expression/statement, it means it will be ignored by compiler unless it is called with --cfg something.

You can turn compiler features on with #[feature] attribute, but it doesn't have anything in common with #[cfg] and cfg!.

The cfg!() macro just returns true if the compiler is called with --cfg something, false otherwise. It's like a function, which value is controlled with the compiler's argument.

Upd. As mentioned above, I missed the point cfg!()/#[cfg] can contain more complex compile-time "expression", including checks for turned on features. But the main point is still this: #[cfg] is like #ifdef FEATURE ... #endif (conditional compilation) on steroids, and cfg!() is like a "function" with boolean output.

2 Likes

Alternatively, you could use #[cfg(..)] with a function:

#[cfg(not(feature = "testfeature"))]
fn hello() {
    println!("Hello, feature-less world! {:?}", SomeEnum::SomeStuff)
}

#[cfg(feature = "testfeature")]
fn hello() {
    println!("Hello, feature world! {:?}", SomeEnum::SomeFeature)
}

fn main() {
    hello()
}

Playpen

3 Likes

I've come up with this workaround: https://github.com/rust-gnome/examples/blob/master/src/gtktest.rs#L10-30

It can be used in expressions:

let menu_button = with_gtk_3_10!(gtk::MenuButton::new().unwrap());
2 Likes

4 replies, 4 different explanations, several hints and possible ways to do it, all in a few seconds, the rust community is awesome! Thanks, guys! :slight_smile:

1 Like

Hi, I am wondering what the command line to use this?

I used rustc --cfg "testfeature" demo.rs and rustc demo.rs, but they returned the same result, they are printing Hello, feature-less world! SomeStuff

Here is the code for demo.rs

#[derive(Debug)]
enum SomeEnum {
    SomeStuff,
    #[cfg(feature = "testfeature")]
    SomeFeature
}

fn main() {
    match () {
        #[cfg(not(feature = "testfeature"))]
        () => println!("Hello, feature-less world! {:?}", SomeEnum::SomeStuff),
        #[cfg(feature = "testfeature")]
        () => println!("Hello, feature world! {:?}", SomeEnum::SomeFeature),
    }
}

I am wondering what the command line to use this?

rustc --cfg 'feature="testfeature"' foo.rs

This is the same thing that Cargo does. Check cargo build --features foo -v.

1 Like

Thanks @japaric .

The commandline works well, however, it doesn't work for the cargo,
In case of the cargo,

I add the lines into the Cargo.toml

[features]
testfeature = []

when I use cargo build --features testfeature -v,

 Compiling compilerfeatures v0.1.0 (file:///home/huang/Desktop/Rust/test/compilerfeatures)
     Running `rustc --crate-name compilerfeatures src/main.rs --crate-type bin --emit=dep-info,link -C debuginfo=2 --cfg 'feature="testfeature"' -C metadata=a37e4aaf4f1f13fd -C extra-filename=-a37e4aaf4f1f13fd --out-dir /home/huang/Desktop/Rust/test/compilerfeatures/target/debug/deps -L dependency=/home/huang/Desktop/Rust/test/compilerfeatures/target/debug/deps`

but the result of cargo run is

Hello, feature-less world! SomeStuff

which is the same as binary built by cargo build -v

Compiling compilerfeatures v0.1.0 (file:///home/huang/Desktop/Rust/test/compilerfeatures)
     Running `rustc --crate-name compilerfeatures src/main.rs --crate-type bin --emit=dep-info,link -C debuginfo=2 -C metadata=3c639b66f2218d68 -C extra-filename=-3c639b66f2218d68 --out-dir /home/huang/Desktop/Rust/test/compilerfeatures/target/debug/deps -L dependency=/home/huang/Desktop/Rust/test/compilerfeatures/target/debug/deps`
    Finished dev [unoptimized + debuginfo] target(s) in 1.4 secs

I compared the two command line messages, the first one with testfeature has a --cfg 'feature="testfeature"' in it which the second one don't have this.

Typo? testfeatures -> testfeature.

It works for me:

$ cargo run --features testfeature -v
   Compiling hello v0.1.0 (file:///home/japaric/tmp/hello)
     Running `rustc --crate-name hello src/main.rs --crate-type bin --emit=dep-info,link -C debuginfo=2 --cfg 'feature="testfeature"' -C metadata=b83a698985ab3cff -C extra-filename=-b83a698985ab3cff --out-dir /home/japaric/tmp/hello/target/debug/deps -L dependency=/home/japaric/tmp/hello/target/debug/deps -Zincremental=/home/japaric/tmp/hello/target/debug/incremental`
warning: variant is never used: `SomeStuff`
 --> src/main.rs:3:5
  |
3 |     SomeStuff,
  |     ^^^^^^^^^
  |
  = note: #[warn(dead_code)] on by default

    Finished dev [unoptimized + debuginfo] target(s) in 3.33 secs
     Running `target/debug/hello`
Hello, feature world! SomeFeature

$ cargo run -v
   Compiling hello v0.1.0 (file:///home/japaric/tmp/hello)
     Running `rustc --crate-name hello src/main.rs --crate-type bin --emit=dep-info,link -C debuginfo=2 -C metadata=22b43a0e111882bc -C extra-filename=-22b43a0e111882bc --out-dir /home/japaric/tmp/hello/target/debug/deps -L dependency=/home/japaric/tmp/hello/target/debug/deps -Zincremental=/home/japaric/tmp/hello/target/debug/incremental`
    Finished dev [unoptimized + debuginfo] target(s) in 3.15 secs
     Running `target/debug/hello`
Hello, feature-less world! SomeStuff

Thanks @japaric for the patience and quick reply, you helped me out.

The error I made was I was using cargo run,
I should use cargo run --features testfeature .

To summarize, to enable the feature,

  1. one way is to use rustc --cfg 'feature="testfeature"' demo.rs
  2. If your are using cargo, you should do:
    • add lines to the Cargo.toml

[features]
testfeature =

- build it with `cargo build --features testfeature -v`
- run it with `cargo run --features testfeature -v`