How can I override a constant via a compiler option?

No, you can't define constants (read: const bindings) with a compiler flag. But you can use the env! macro for something similar. It reads some environment variable at compile time.

const MAX_DIMENSIONS_RAW: &'static str = env!("MAX_DIMENSIONS");

Sadly, this returns a string and not an integer. Furthermore we can't yet call arbitrary functions (like parse) at compile time to calculate a constant. You could use lazy_static to achieve something similar:

lazy_static! {
    static ref MAX_DIMENSIONS: usize = MAX_DIMENSIONS_RAW.parse().unwrap();
}

Of course you should add proper error handling. If your user doesn't need to define the environment variable, you can use option_env!.

With this approach, you can pass the setting at build time:

$ MAX_DIMENSIONS=1000 cargo build

Building on Lukas Kalbertodt's answer, you can get the environment variable as a constant number with some extra indirection, namely by using a build script.

build.rs

use std::{env, fs::File, io::Write, path::Path};

fn main() {
    let out_dir = env::var("OUT_DIR").expect("No out dir");
    let dest_path = Path::new(&out_dir).join("constants.rs");
    let mut f = File::create(&dest_path).expect("Could not create file");

    let max_dimensions = option_env!("MAX_DIMENSIONS");
    let max_dimensions = max_dimensions
        .map_or(Ok(10_000), str::parse)
        .expect("Could not parse MAX_DIMENSIONS");

    write!(&mut f, "const MAX_DIMENSIONS: usize = {};", max_dimensions)
        .expect("Could not write file");
    println!("cargo:rerun-if-env-changed=MAX_DIMENSIONS");
}

main.rs

include!(concat!(env!("OUT_DIR"), "/constants.rs"));

fn main() {
    println!("The value is {} ({})", MAX_DIMENSIONS, MAX_DIMENSIONS + 1);
}
$ cargo run
The value is 10000 (10001)

$ MAX_DIMENSIONS=17 cargo run
The value is 17 (18)

$ MAX_DIMENSIONS=1 cargo run
The value is 1 (2)

Tags:

Rust