Rust:初始化库中的静态变量/引用?

1 static-initialization lazy-initialization rust lazy-static

我是 Rust 新手。我正在尝试在库中创建一个静态变量,DATA以便Vec<u8>在编译库后对其进行初始化。然后我将该库包含在主代码中,希望能够DATA直接使用而无需再次调用init_data()。这是我尝试过的:

my_lib.rs:

use lazy_static::lazy_static;

pub fn init_data() -> Vec<u8> {
    // some expensive calculations
}

lazy_static! {
    pub static ref DATA: Vec<u8> = init_data();  // supposed to call init_data() only once during compilation
}
Run Code Online (Sandbox Code Playgroud)

主要.rs:

use my_lib::DATA;
call1(&DATA);  // use DATA here without calling init_data()
call2(&DATA);
Run Code Online (Sandbox Code Playgroud)

但事实证明,init_data()仍然是在调用main.rs。这段代码有什么问题?


更新:正如 Ivan C 指出的那样,lazy_static它不在编译时运行。那么,“预加载”数据的正确选择是什么?

Kev*_*eid 5

这里有两个问题:类型的选择和分配。

\n

不可能在编译时构造 a Vec、 aBox或任何其他需要堆分配的类型,因为此时堆分配器和堆尚不存在。相反,您必须使用引用类型,它可以指向二进制文件中而不是运行时堆中分配的数据,或者没有任何引用的数组(如果数据不太大)。

\n

接下来,我们需要一种方法来执行计算。理论上,最干净的选项是不断评估\xe2\x80\x94\xc2\xa0 在编译时直接执行部分代码。

\n
static DATA: &\'static [u8] = {\n    // code goes here\n};\n
Run Code Online (Sandbox Code Playgroud)\n

然而,在当前稳定的 Rust 版本(我正在写这篇文章时为 1.58.1)中,常量评估非常有限,因为您无法执行任何类似于dropping 值的操作,或使用属于特征的任何函数。它仍然可以做一些事情,主要是整数算术或构造其他“几乎文字”数据;例如:

\n
const N: usize = 10;\nstatic FIRST_N_FIBONACCI: &\'static [u32; N] = &{\n    let mut array = [0; N];\n    array[1] = 1;\n    let mut i = 2;\n    while i < array.len() {\n        array[i] = array[i - 1] + array[i - 2];\n        i += 1;\n    }\n    array\n};\n\nfn main() {\n    dbg!(FIRST_N_FIBONACCI);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

如果您的计算无法使用 const 计算来表达,那么您将需要以另一种方式执行它:

\n
    \n
  • 过程宏是有效的编译器插件,它们可以执行任意计算,但它们的输出是生成 Rust 语法。因此,过程宏可以使用预先计算的数据生成数组文字。

    \n

    程序宏的主要限制是它们必须在专用的板条箱中定义(因此,如果您的项目是一个库板条箱,那么现在将是两个)。

    \n
  • \n
  • 构建脚本是普通的 Rust 代码,可以编译或生成主编译使用的文件。它们不与编译器交互,而是在编译开始之前由 Cargo 运行。

    \n
  • \n
\n

(与 const 评估不同,构建脚本和 proc 宏都不能使用正在构建的包中定义的任何类型或常量;它们可以读取源代码,但它们运行得太早而无法使用包中的其他项目他们自己的代码。)

\n

在您的情况下,因为您想要预先计算一些[u8]数据,我认为最简单的方法是添加一个将数据写入文件的构建脚本,之后您的正常代码可以使用include_bytes!.

\n