我正在学习Rust-wasm教程。其中你构建了一个生命克隆游戏,并且目前正在做";用一艘宇宙飞船初始化宇宙";运动
为了实现这艘船,我启动了一个模块,该模块保存船只数据和相关功能,将船只绘制到网格中。在这个模块中,我想存储一些预先制作好的众所周知的船只/图案,例如铜头船。
对于数据结构,我提出了以下结构:
// life_patterns.rs
pub struct LifePattern {
width: u32,
height: u32,
data: Vec<u8>,
}
现在我想将实际数据硬编码到模块中。来自JavaScript背景,我想到了:
// life_patterns.rs
pub const COPPERHEAD: LifePattern = LifePattern {
width: 10,
height: 13,
data: vec![
0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
1, 1, 0, 1, 0, 0, 1, 0, 1, 1,
0, 0, 0, 1, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
],
}
然后我想把图案画到一个现有的网格上,就像这样:
// lib.rs
life_patterns::draw(grid, start_index, life_patterns::COPPERHEAD);
我的解决方案没有编译错误消息:
allocations are not allowed in constants E0010
calls in constants are limited to constant functions, tuple structs and tuple variants E0015
现在我的问题是,如何以惯用的方式正确地对life_patterns模块中的铜头船的数据进行硬编码?
问这个问题的一种更普遍的方式可以是:;如何硬编码Vec<u8>和Rust模块中的两个u32">
为了接近您在问题中显示的用法,我将使用lazy_static。
其目的是在初始化与const
不兼容时提供类似于const
的东西;然后,它在运行时一次性发生。
编辑
@Caesar的一句非常有趣的话建议依赖once_cell
,这应该成为标准。
另一个答案提出了一个可读的模式,在我看来这是一个非常好的主意。
该示例保留了原始解决方案作为注释,并在考虑前两个注释的情况下提出了另一个解决方案。
pub struct LifePattern {
width: u32,
height: u32,
data: Vec<u8>,
}
/*
lazy_static::lazy_static! {
pub static ref COPPERHEAD: LifePattern = LifePattern {
width: 10,
height: 13,
data: vec![
0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
1, 1, 0, 1, 0, 0, 1, 0, 1, 1,
0, 0, 0, 1, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
],
};
}
*/
static COPPERHEAD: once_cell::sync::Lazy<LifePattern> =
once_cell::sync::Lazy::new(|| LifePattern {
width: 10,
height: 13,
data: "
____XX____
___XXXX___
__________
__XXXXXX__
___XXXX___
__________
__XX__XX__
XX_X__X_XX
___X__X___
__________
__________
____XX____
____XX____"
.chars()
.map(|c| if c == 'X' { 1 } else { 0 })
.collect(),
});
fn main() {
let pattern: &LifePattern = &COPPERHEAD;
println!("with={}", pattern.width);
println!("height={}", pattern.height);
println!("data={:?}", pattern.data);
}
我想说,让格式尽可能地让人可读,并让计算机在运行时进行转换。
// pattern is clearly visible
pub const COPPERHEAD: &'static[&'static str] = &[
"____11____",
"___1111___",
"__________",
"__111111__",
"___1111___",
"__________",
"__11__11__",
"11_1__1_11",
"___1__1___",
"__________",
"__________",
"____11____",
"____11____",
];
pub struct LifePattern {
width: u32,
height: u32,
data: Vec<u8>,
}
impl From<&[&str]> for LifePattern {
fn from(pattern: &[&str]) -> Self {
let width = pattern.first().map(|s| s.len()).unwrap_or(0) as u32;
let height = pattern.len() as u32;
let mut data = Vec::with_capacity(width as usize * height as usize);
for line in pattern {
for c in line.chars() {
data.push((c != '_') as u8);
}
}
Self {
width,
height,
data,
}
}
}
然后你做
life_patterns::draw(grid, start_index, life_patterns::COPPERHEAD.into());