如何通过编译器选项覆盖常量?

jba*_*ter 4 rust

是否可以在源代码中定义一个可以被编译器标志覆盖的常量?也就是说,类似#define在C预处理器中设置带有-D key=val编译器选项的值。

我已经阅读了有关通过#[cfg(...)]属性进行条件编译的信息,但这似乎仅支持布尔值。我想允许用户在编译期间设置常量的值。

像这样:

#[from_cfg("max_dimensions")]
const MAX_DIMENSIONS: usize = 100_000;
Run Code Online (Sandbox Code Playgroud)

Luk*_*odt 5

不,您不能const使用编译器标志定义常量(读取:绑定)。但是您可以将env!用于类似的东西。它在编译时读取一些环境变量。

const MAX_DIMENSIONS_RAW: &'static str = env!("MAX_DIMENSIONS");
Run Code Online (Sandbox Code Playgroud)

可悲的是,这将返回字符串而不是整数。此外,我们还不能parse在编译时调用任意函数(如)来计算常量。您可以lazy_static用来实现类似的目的:

lazy_static! {
    static ref MAX_DIMENSIONS: usize = MAX_DIMENSIONS_RAW.parse().unwrap();
}
Run Code Online (Sandbox Code Playgroud)

当然,您应该添加适当的错误处理。如果您的用户不需要定义环境变量,则可以使用option_env!

使用这种方法,您可以在构建时通过设置:

$ MAX_DIMENSIONS=1000 cargo build
Run Code Online (Sandbox Code Playgroud)


She*_*ter 5

Lukas Kalbertodt的答案的基础上,您可以通过一些额外的间接方式(即通过使用构建脚本)将环境变量获取为常数。

构建器

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");
}
Run Code Online (Sandbox Code Playgroud)

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

fn main() {
    println!("The value is {} ({})", MAX_DIMENSIONS, MAX_DIMENSIONS + 1);
}
Run Code Online (Sandbox Code Playgroud)
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");
}
Run Code Online (Sandbox Code Playgroud)