我可以创建自己的条件编译属性吗?

Mau*_*ser 3 conditional-compilation rust

在我的 crate 中有几种方法可以做一些事情,有些可以快速执行,有些二进制大小很小,有些还有其他优点,所以我为所有这些都提供了用户界面。未使用的函数将被编译器优化掉。我的 crate 中的内部函数也必须使用这些接口,我希望它们在编译时尊重用户的选择。

有条件编译属性,如target_os,它存储一个值,如linuxwindows。例如prefer_method,我如何创建这样的属性,以便我和用户可以像在以下代码片段中一样使用它?

我的箱子:

#[cfg(not(any(
    not(prefer_method),
    prefer_method = "fast",
    prefer_method = "small"
)))]
compile_error("invalid `prefer_method` value");

pub fn bla() {
    #[cfg(prefer_method = "fast")]
    foo_fast();

    #[cfg(prefer_method = "small")]
    foo_small();

    #[cfg(not(prefer_method))]
    foo_default();
}

pub fn foo_fast() {
    // Fast execution.
}

pub fn foo_small() {
    // Small binary file.
}

pub fn foo_default() {
    // Medium size, medium fast.
}
Run Code Online (Sandbox Code Playgroud)

用户箱:

#[prefer_method = "small"]
extern crate my_crate;

fn f() {
    // Uses the `foo_small` function, the other `foo_*` functions will not end up in the binary.
    my_crate::bla();

    // But the user can also call any function, which of course will also end up in the binary.
    my_crate::foo_default();
}
Run Code Online (Sandbox Code Playgroud)

我知道有--cfg属性,但 AFAIK 这些只代表布尔标志,而不是枚举值,当只有一个枚举值有效时,它们允许设置多个标志。

And*_*par 5

首先,该--cfg标志支持使用语法的键值对--cfg 'prefer_method="fast"'。这将允许您编写如下代码:

#[cfg(prefer_method = "fast")]
fn foo_fast() { }
Run Code Online (Sandbox Code Playgroud)

您还可以从构建脚本设置这些 cfg 选项。例如:

// build.rs
fn main() {
    println!("cargo:rustc-cfg=prefer_method=\"method_a\"");
}
Run Code Online (Sandbox Code Playgroud)
// src/main.rs
#[cfg(prefer_method = "method_a")]
fn main() {
    println!("It's A");
}

#[cfg(prefer_method = "method_b")]
fn main() {
    println!("It's B");
}

#[cfg(not(any(prefer_method = "method_a", prefer_method = "method_b")))]
fn main() {
    println!("No preferred method");
}
Run Code Online (Sandbox Code Playgroud)

上面的代码将生成一个打印“It's A”的可执行文件。

没有像您建议的指定 cfg 设置那样的语法。将这些选项公开给您的 crate 用户的最佳方式是通过Cargo features

例如:

# Library Cargo.toml
# ...
[features]
method_a = []
method_b = []
Run Code Online (Sandbox Code Playgroud)
// build.rs
fn main() {
    // prefer method A if both method A and B are selected
    if cfg!(feature = "method_a") {
        println!("cargo:rustc-cfg=prefer_method=\"method_a\"");
    } else if cfg!(feature = "method_b") {
        println!("cargo:rustc-cfg=prefer_method=\"method_b\"");
    }
}
Run Code Online (Sandbox Code Playgroud)
# User Cargo.toml
# ...
[dependencies.my_crate]
version = "..."
features = ["method_a"]
Run Code Online (Sandbox Code Playgroud)

但是,在这种情况下,我建议您直接在代码中使用 Cargo 功能(即#[cfg(feature = "fast")]),而不是添加构建脚本,因为 Cargo 功能和添加的 rustc-cfg 之间存在一一对应的关系。