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
它不在编译时运行。那么,“预加载”数据的正确选择是什么?
这里有两个问题:类型的选择和分配。
\n不可能在编译时构造 a Vec
、 aBox
或任何其他需要堆分配的类型,因为此时堆分配器和堆尚不存在。相反,您必须使用引用类型,它可以指向二进制文件中而不是运行时堆中分配的数据,或者没有任何引用的数组(如果数据不太大)。
接下来,我们需要一种方法来执行计算。理论上,最干净的选项是不断评估\xe2\x80\x94\xc2\xa0 在编译时直接执行部分代码。
\nstatic DATA: &\'static [u8] = {\n // code goes here\n};\n
Run Code Online (Sandbox Code Playgroud)\n然而,在当前稳定的 Rust 版本(我正在写这篇文章时为 1.58.1)中,常量评估非常有限,因为您无法执行任何类似于drop
ping 值的操作,或使用属于特征的任何函数。它仍然可以做一些事情,主要是整数算术或构造其他“几乎文字”数据;例如:
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过程宏是有效的编译器插件,它们可以执行任意计算,但它们的输出是生成 Rust 语法。因此,过程宏可以使用预先计算的数据生成数组文字。
\n程序宏的主要限制是它们必须在专用的板条箱中定义(因此,如果您的项目是一个库板条箱,那么现在将是两个)。
\n构建脚本是普通的 Rust 代码,可以编译或生成主编译使用的文件。它们不与编译器交互,而是在编译开始之前由 Cargo 运行。
\n(与 const 评估不同,构建脚本和 proc 宏都不能使用正在构建的包中定义的任何类型或常量;它们可以读取源代码,但它们运行得太早而无法使用包中的其他项目他们自己的代码。)
\n在您的情况下,因为您想要预先计算一些[u8]
数据,我认为最简单的方法是添加一个将数据写入文件的构建脚本,之后您的正常代码可以使用include_bytes!
.
归档时间: |
|
查看次数: |
3195 次 |
最近记录: |