在 Rust 模块中使用一些元数据对字节向量进行硬编码的惯用方法是什么?
What is the idiomatic way to hardcode a byte vector with some metadata in a Rust module?
我正在关注 Rust wasm tutorial. in which you build a game-of-life 克隆,目前正在做“用一艘 space 飞船初始化宇宙”练习。
为了实现这艘船,我启动了一个模块,该模块包含船舶数据和相关函数,用于将一艘船绘制到网格中。在这个模块中,我想存储一些预先制作的众所周知的 ships/patterns 例如 copperhead ship.
对于数据结构,我提出了以下结构:
// 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 模块中正确硬编码铜斑蛇船的数据?
一个更通用的提问方式可能是:“我如何在 Rust 模块中惯用地硬编码一个 Vec 和两个 u32?”
我会说,格式尽可能human-readable,让计算机在运行时转换它。
// 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());
为了接近您在问题中显示的用法,我会使用 lazy_static。
它的目的是在初始化与const
不兼容时提供类似于const
的东西;然后它在 run-time.
发生一次
编辑
@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);
}
我正在关注 Rust wasm tutorial. in which you build a game-of-life 克隆,目前正在做“用一艘 space 飞船初始化宇宙”练习。
为了实现这艘船,我启动了一个模块,该模块包含船舶数据和相关函数,用于将一艘船绘制到网格中。在这个模块中,我想存储一些预先制作的众所周知的 ships/patterns 例如 copperhead ship.
对于数据结构,我提出了以下结构:
// 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 模块中正确硬编码铜斑蛇船的数据?
一个更通用的提问方式可能是:“我如何在 Rust 模块中惯用地硬编码一个 Vec
我会说,格式尽可能human-readable,让计算机在运行时转换它。
// 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());
为了接近您在问题中显示的用法,我会使用 lazy_static。
它的目的是在初始化与const
不兼容时提供类似于const
的东西;然后它在 run-time.
编辑
@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);
}