在 Rust 中,我可以在不对值进行硬编码的情况下实例化我的 const 数组吗?编译时评估?
In Rust, can I instantiate my const array without hard-coding in the values? Compile-time evaluation?
我正在尝试用 Rust 实例化一个数组。这是我可以在运行时执行的一种方法:
let mut t = [0_u32; 65];
for i in 0..t.len() {
t[i] = ((i as f64).sin().abs() * 2.0_f64.powf(32.0)).floor() as u32;
}
但是,由于我永远不会更改此数组的值,而且我会经常使用这些值,所以我认为这可能是一个很好的机会来探索 const
在 Rust 中完成的编译时评估工作。我可以让它在编译时计算数组,并将结果存储在程序数据中,以便在运行时立即运行。
我的第一步是创建常量数组。
const T: [u32; 65] = [0; 65];
嗯,这样不行。我已经用全零实例化了它。那是不对的。接下来,我想也许我应该做一个可以实例化数组的常量函数。
const fn sine_table() -> [u32; 65] {
let mut t = [0_u32; 65];
let mut i = 0;
loop {
if i > 65 {
break;
}
// Do the math...
}
t
}
这就是我卡住的地方。从我读到的内容来看,常量函数内的循环仍然只在夜间出现,我试图暂时坚持使用稳定的 Rust,以避免以后出现意外。那么,这会把我留在哪里?我目前可以在稳定版中做什么,以及在 nightly、RFC 等中即将发布的内容?我的下一个想法是研究宏,但我还没有足够舒服地进入那个兔子洞而不知道它是否会富有成果。我的最终目标是使这个数组成为常量,而不必手动输入 65 个值。
到目前为止,在 Rust Stable 上,这是不可能的(你需要 const fn 表达式才能在编译时工作)。
但幸运的是,这个用例有一个 "in-between" 解决方案(我也经常使用),即 lazy_static
宏。
基本上它是一个延迟计算的运行时表达式,仅在第一次访问时计算。
https://docs.rs/lazy_static/1.4.0/lazy_static/
使用此宏,您的代码将如下所示:
use lazy_static::lazy_static;
const SINE_TABLE_SIZE: usize = 65;
lazy_static! {
pub static ref SINE_TABLE: [u32; SINE_TABLE_SIZE] = {
let mut table = [0_u32; SINE_TABLE_SIZE];
for i in 0..SINE_TABLE_SIZE {
table[i] = ((i as f64).sin().abs() * 2.0f64.powf(32.)).floor() as u32;
}
table
};
}
Rust 游乐场示例 link:https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=61146c5f7de2c9ee1cbcd724dd1a730f
免责声明:我不是 lazy_static 的作者,也与他们没有任何关系。
Cargo 支持 build.rs
个编译后的文件和 运行 个整体编译前的文件。对于您来说,最简单的选择是使用它来生成您想要使用的 table。
Rust 文档有一个 code generation using this method 的示例,因此如果您使用代码并使用它来生成数组,就可以开始了。您可以将 build = "build.rs"
放入 Cargo.toml
并让 build.rs
成为:
use std::io::{Result, Write};
fn main() -> Result<()> {
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("sin_abs_const.rs");
let mut f = File::create(&dest_path).unwrap();
write!(f, "const T: [u32; 65] = [\n")?;
for i in 0..64 {
write!(f, " {},\n", ((i as f64).sin().abs() * 2.0_f64.powf(32.0)).floor() as u32)?;
}
write!(f, "];\n")?;
Ok(())
}
然后你可以加载那个构建的文件。
我正在尝试用 Rust 实例化一个数组。这是我可以在运行时执行的一种方法:
let mut t = [0_u32; 65];
for i in 0..t.len() {
t[i] = ((i as f64).sin().abs() * 2.0_f64.powf(32.0)).floor() as u32;
}
但是,由于我永远不会更改此数组的值,而且我会经常使用这些值,所以我认为这可能是一个很好的机会来探索 const
在 Rust 中完成的编译时评估工作。我可以让它在编译时计算数组,并将结果存储在程序数据中,以便在运行时立即运行。
我的第一步是创建常量数组。
const T: [u32; 65] = [0; 65];
嗯,这样不行。我已经用全零实例化了它。那是不对的。接下来,我想也许我应该做一个可以实例化数组的常量函数。
const fn sine_table() -> [u32; 65] {
let mut t = [0_u32; 65];
let mut i = 0;
loop {
if i > 65 {
break;
}
// Do the math...
}
t
}
这就是我卡住的地方。从我读到的内容来看,常量函数内的循环仍然只在夜间出现,我试图暂时坚持使用稳定的 Rust,以避免以后出现意外。那么,这会把我留在哪里?我目前可以在稳定版中做什么,以及在 nightly、RFC 等中即将发布的内容?我的下一个想法是研究宏,但我还没有足够舒服地进入那个兔子洞而不知道它是否会富有成果。我的最终目标是使这个数组成为常量,而不必手动输入 65 个值。
到目前为止,在 Rust Stable 上,这是不可能的(你需要 const fn 表达式才能在编译时工作)。
但幸运的是,这个用例有一个 "in-between" 解决方案(我也经常使用),即 lazy_static
宏。
基本上它是一个延迟计算的运行时表达式,仅在第一次访问时计算。
https://docs.rs/lazy_static/1.4.0/lazy_static/
使用此宏,您的代码将如下所示:
use lazy_static::lazy_static;
const SINE_TABLE_SIZE: usize = 65;
lazy_static! {
pub static ref SINE_TABLE: [u32; SINE_TABLE_SIZE] = {
let mut table = [0_u32; SINE_TABLE_SIZE];
for i in 0..SINE_TABLE_SIZE {
table[i] = ((i as f64).sin().abs() * 2.0f64.powf(32.)).floor() as u32;
}
table
};
}
Rust 游乐场示例 link:https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=61146c5f7de2c9ee1cbcd724dd1a730f
免责声明:我不是 lazy_static 的作者,也与他们没有任何关系。
Cargo 支持 build.rs
个编译后的文件和 运行 个整体编译前的文件。对于您来说,最简单的选择是使用它来生成您想要使用的 table。
Rust 文档有一个 code generation using this method 的示例,因此如果您使用代码并使用它来生成数组,就可以开始了。您可以将 build = "build.rs"
放入 Cargo.toml
并让 build.rs
成为:
use std::io::{Result, Write};
fn main() -> Result<()> {
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("sin_abs_const.rs");
let mut f = File::create(&dest_path).unwrap();
write!(f, "const T: [u32; 65] = [\n")?;
for i in 0..64 {
write!(f, " {},\n", ((i as f64).sin().abs() * 2.0_f64.powf(32.0)).floor() as u32)?;
}
write!(f, "];\n")?;
Ok(())
}
然后你可以加载那个构建的文件。