Reputation: 75
I'm very new in Rust, and I would like to implement a 2D map. So I have a map structure with inside width, height and the data pointer with unknown size until calling a load_map function which parse a file and then initialize the map structure. In C language I would do:
struct map {
uint32_t width;
uint32_t height;
uint8_t *data;
};
int load_map(struct map *m)
{
... parse a file and found a map with height=width=8
m->width = 8;
m->height = 8;
// allocate map data using the map size we found
m->data = (uint8_t *)malloc(m->width * m->height);
...
}
In Rust, I have no idea how to achieve something that simple ! Here is my failing attempt:
struct Map<'a> {
width: usize,
height: usize,
data: &'a mut u8
}
fn load_map() -> Map<'static>
{
//code that parse a file and found a map with height=width=8
...
const w:usize = 8;
const h:usize = 8;
//allocate the map data
let mut data = [0;w*h];
// create the map structure
let mut m = Map {
width: w,
height: h,
data: data
};
m
}
fn main() {
let mut m: Map;
m = load_map();
println!("Hello, world! {} {}", m.height, m.width);
}
But cargo is not happy and throw me this error:
error[E0308]: mismatched types
--> src/main.rs:16:15
|
16 | data: data
| ^^^^ expected `&mut u8`, found array `[{integer}; 64]`
error: aborting due to previous error
I do understand the error: rust is expecting the same type of data for data. But I have no clue on how to achieve that. The Map struct is generic and there is no way we can know the data size before parsing the file. It seems that we should be able to declare the data bytes in init_map and give ownership to Map structure, right ? But how ?
Upvotes: 5
Views: 4525
Reputation: 43912
Disclaimer: I'm very new to Rust myself; I just happen to be working on this exact same problem and have thus learned a fair bit about it. There may be better solutions or alternatives to what I've suggested here.
If in C you have a pointer to an array
uint8_t *data;
which is initialized by malloc
and expected to have the same lifetime as the containing structure, then the way you write that in Rust is not data: &u8
but:
data: Box<[u8]>,
This is different from your attempt in two ways:
[u8]
instead of u8
, so it stores many elements instead of exactly one.Box<...>
instead of &...
, so it owns what it refers to.Creating a Box allocates its contents on the heap and thus directly corresponds to the use of malloc
. Here I've modified your code to show this working and also take care of all the warnings:
struct Map {
width: usize,
height: usize,
data: Box<[u8]>,
}
fn load_map() -> Map {
const W: usize = 8;
const H: usize = 8;
//allocate the map data
let data = [0;W*H];
// create the map structure
let m = Map {
width: W,
height: H,
data: Box::new(data)
};
m
}
fn main() {
let m: Map = load_map();
println!("Hello, world! {} {} {}", m.height, m.width, m.data[0]);
}
Notice also that the explicit lifetime parameters <'a>
and 'static
are gone; they are not needed now, and they would not have worked (because something allocated by a function cannot be 'static
unless you choose to leak it).
However, this still requires a const
width and height in order to construct the array, which will not work as soon as the width and height are read from the map file. In order to fix that, you'll need to construct the array (or more precisely, array slice) in a different way; one option is to use a Vec
constructor (and then simplify it into an array slice because you're not planning to change the length):
data: vec![0; w * h].into_boxed_slice(),
Upvotes: 3