🎓 A1Learner
🔍

Rust Custom Cfg Names

A custom cfg name is a condition name that a project invents for itself. A custom name covers a choice the machine cannot know, like a beta switch or a licence tier.

A build script turns the name on

One printed line in build.rs sets the name for the whole build. The double colon is the modern spelling. The older cargo: form still works:

// build.rs
fn main() {
    println!("cargo::rustc-cfg=turbo_mode");
}

Use the name like any built-in

Code asks with cfg! or guards items with #[cfg], exactly as it does with target_os. With the script above in place, this program prints on:

if cfg!(turbo_mode) {
    println!("turbo = on");
} else {
    println!("turbo = off");
}

The warning: unexpected cfg

Miss one step out, and the build still works — but the compiler warns. It has never heard of your name, and it says so:

// warning: unexpected `cfg` condition name: `turbo_mode`

Declare the name in the same script

A second printed line registers the name as legal. The build then runs with no warning:

// build.rs
fn main() {
    println!("cargo::rustc-check-cfg=cfg(turbo_mode)");
    println!("cargo::rustc-cfg=turbo_mode");
}

Or declare it in Cargo.toml

A lints table does the same job, in the open. This is also where a name set by hand with RUSTFLAGS gets declared:

[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(turbo_mode)'] }

Values can be declared too

A name may carry values, and you can list the legal ones. A value outside the list earns the same warning, so typos get caught:

println!("cargo::rustc-check-cfg=cfg(flavour, values(\"fast\", \"slow\"))");

Example

// build.rs
// fn main() {
//     println!("cargo::rustc-check-cfg=cfg(turbo_mode)");
//     println!("cargo::rustc-cfg=turbo_mode");
// }
// src/main.rs:
fn main() {
    if cfg!(turbo_mode) {
        println!("turbo = on");
    } else {
        println!("turbo = off");
    }
}

Key points

  • Setting and declaring are two jobs.
  • rustc-cfg switches a name on.
  • rustc-check-cfg tells the compiler the name exists.
  • The lints table can do that job too
  • In a Cargo build, even a typo in a built-in value warns: cfg!(target_os = "linus") is flagged, and the warning lists the legal values
  • A name set by hand travels in RUSTFLAGS: RUSTFLAGS="--cfg turbo_mode" cargo build.
  • It still needs declaring, or the warning returns
  • A cfg name is crate-local.
  • It switches code inside your crate only.
  • It never becomes part of your public API.
  • A feature does become part of it
  • Each crate's build script speaks only for its own package.
  • Built-in cfg names describe the machine.
  • A custom cfg name is a project-invented condition for choices beyond the machine.

In this example, the build script does two jobs. Its first printed line declares turbo_mode, so the compiler accepts the name in silence. Its second line switches the name on for this build. The main program then asks with cfg! and prints turbo = on. Delete the declaration line and the same build prints a warning about an unexpected cfg name. Delete the rustc-cfg line instead, and the program quietly prints turbo = off.

Note

Turning a name on for one crate does not turn it on for its neighbours.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

Your build warns: unexpected `cfg` condition name: `turbo_mode`. What is missing?