Pass numeric compile time arguments

I'd like to compile a project multiple times with different settings (for different environments).
Is there a simple way to pass an argument to the compiler that will be used as a numeric constant?

Something like

$ SAMPLE_RATE=44100 cargo build

So far I've tried:

pub const SAMPLE_RATE: u32 = env!("SAMPLE_RATE").parse::<u32>().unwrap();

(this naturally fails because parse cannot be called in a constant manner)

I thought about using a build.rs and write out a single-lined source file based on the environment variables during the build:

pub const SAMPLE_RATE: u32 = $$to be replaced by build-script$$;

This would work, but cargo's documentation states:

Build scripts may save any output files in the directory specified in the OUT_DIR environment variable. Scripts should not modify any files outside of that directory."

… and it's a bit cumbersome

You can write your own parsing function that is const:

const fn parse_u32(s: &str)->u32 {
    let mut out:u32 = 0;
    let mut i:usize = 0;
    while i<s.len() {
        out *= 10;
        out += (s.as_bytes()[i] - b'0') as u32;
        i += 1;
    }
    out
}

pub const SAMPLE_RATE: u32 = parse_u32("44100");

fn main() {
    dbg!(SAMPLE_RATE);
}

Playground

3 Likes

You could write your own procedural macro for parsing the environment variable at compile-time, it's the cleanest solution if you are willing to pay that price upfront.

1 Like

2e71828 and H2CO3 have reasonable solutions (another is to use good old lazy_static or once_cell), but I do think it would be nice if the standard library had a macro like env! that took care of this for you.

1 Like

Thank you all for your input!

@2e71828's solution seems to be the most reasonable workaround until there's some std macro to cover those cases!

@cole-miller This was meant to be an aggressive optimization where the result was required to be const (pre-determine buffer sizes, bounds check elision, etc.). Otherwise lazy_static would be a valid solution as well.

1 Like