我是Rust的新手。我试图在库中创建Vec<u8>
的静态变量DATA
,以便在编译lib后对其进行初始化。然后,我将lib包含在主代码中,希望在不再次调用init_data()
的情况下直接使用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
}
main.rs:
use my_lib::DATA;
call1(&DATA); // use DATA here without calling init_data()
call2(&DATA);
但事实证明CCD_ 5在CCD_ 6中仍然被调用。这个代码出了什么问题?
更新:正如Ivan C所指出的,lazy_static
在编译时没有运行。那么,"预加载"数据的正确选择是什么?
这里有两个问题:类型的选择和执行分配。
不可能在编译时构造Vec
、Box
或需要堆分配的任何其他类型,因为此时还不存在堆分配器和堆。相反,您必须使用引用类型,该类型可以指向在二进制文件中而不是在运行时堆中分配的数据,或者没有任何引用的数组(如果数据不是太大(。
接下来,我们需要一种执行计算的方法。理论上,最干净的选项是常量求值——在编译时直接执行部分代码。
static DATA: &'static [u8] = {
// code goes here
};
然而,在当前稳定的Rust版本(我写这篇文章的时候是1.58.1(中,持续评估是非常有限的,因为你不能做任何看起来像drop
ping值的事情,也不能使用任何属于特性的函数。它仍然可以做一些事情,主要是整数运算或构造其他"整数";几乎是字面意义上的";数据例如:
const N: usize = 10;
static FIRST_N_FIBONACCI: &'static [u32; N] = &{
let mut array = [0; N];
array[1] = 1;
let mut i = 2;
while i < array.len() {
array[i] = array[i - 1] + array[i - 2];
i += 1;
}
array
};
fn main() {
dbg!(FIRST_N_FIBONACCI);
}
如果你的计算不能用const求值来表示,那么你需要用另一种方式来执行:
过程宏是有效的编译器插件,它们可以执行任意计算,但它们的输出是生成的Rust语法。所以,过程宏可以使用预先计算的数据生成数组文字。
过程宏的主要限制是它们必须在专用的板条箱中定义(因此,如果您的项目是一个库板条箱,那么现在应该是两个(。
构建脚本是普通的Rust代码,可以编译或生成主编译使用的文件。它们不与编译器交互,而是在编译开始前由Cargo运行。
(与const评估不同,构建脚本和proc宏都不能使用正在构建的机箱中定义的任何类型或常量;它们可以读取源代码,但运行太早,无法在自己的代码中使用机箱中的其他项。(
在您的情况下,因为您想要预计算一些[u8]
数据,我认为最简单的方法是添加一个构建脚本,将数据写入文件,之后,您的普通代码可以使用include_bytes!
从文件中嵌入这些数据。