I am following the Rust wasm tutorial. in which you build a game-of-life clone and am currently doing the "Initialize the universe with a single space ship" exercise.
To implement the ship I started a module which holds the ship data and associated functions to draw a ship to a grid. In this module I want to store some pre-made well known ships/patterns as for example the copperhead ship.
For the data structure I came up with following struct:
// life_patterns.rs
pub struct LifePattern {
width: u32,
height: u32,
data: Vec<u8>,
}
Now I want to hardcode the actual data into the module. Coming from a JavaScript background I came up with:
// 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,
],
}
I want then draw the pattern to an existing grid like so:
// lib.rs
life_patterns::draw(grid, start_index, life_patterns::COPPERHEAD);
My solution doesn't compile with the error messages:
allocations are not allowed in constants E0010
calls in constants are limited to constant functions, tuple structs and tuple variants E0015
So now my question, how do I properly hardcode the data for the copperhead ship in the life_patterns module in an idiomatic way?
A more general way to ask this could be: "How do I hardcode an Vec<u8> and two u32 in a Rust module idiomaticly?"
CodePudding user response:
In order to stay close to the usage you show in your question, I would use lazy_static.
Its purpose is to provide something similar to const
when the initialisation is not compatible with const
; it then happens once for all at run-time.
edit
A very interesting remark of @Caesar suggest relying on once_cell
which should become standard.
The other answer suggests a readable pattern, which is a very good idea in my opinion.
The example keeps the original solution as a comment and suggests another solution considering the two previous remarks.
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);
}
CodePudding user response:
I would say, make the format as human-readable as possible and let the computer convert it at runtime.
// 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,
}
}
}
then you do
life_patterns::draw(grid, start_index, life_patterns::COPPERHEAD.into());