Как реализовать 2D-массив Dynami c внутри структуры в Rust? - PullRequest
2 голосов
/ 06 августа 2020

Я новичок в Rust и хотел бы реализовать 2D-карту. Итак, у меня есть структура карты с внутренней шириной, высотой и указателем данных с неизвестным размером до вызова функции load_map, которая анализирует файл, а затем инициализирует структуру карты. На языке C я бы сделал:

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);
   ...
}

В Rust я понятия не имею, как достичь чего-то настолько простого! Вот моя неудачная попытка:

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);
}

Но машина go недовольна и выдает мне эту ошибку:

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

Я понимаю ошибку: ржавчина ожидает того же типа данных для данных. Но я понятия не имею, как этого добиться. Структура Map имеет общий вид c, и мы не можем узнать размер данных до анализа файла. Кажется, мы должны иметь возможность объявить байты данных в init_map и передать право собственности на структуру Map, верно? Но как?

1 Ответ

2 голосов
/ 06 августа 2020

Предупреждение: я сам новичок в Rust; Я просто работаю над той же самой проблемой и, таким образом, немного узнал о ней. Могут быть лучшие решения или альтернативы тому, что я предложил здесь.

Если в C у вас есть указатель на массив

  uint8_t *data;

, который инициализируется malloc и ожидается, что его время жизни будет таким же, как и у содержащей его структуры, то в Rust вы пишете не data: &u8, а:

    data: Box<[u8]>,

Это отличается от вашей попытки двумя способами:

  1. Он имеет [u8] вместо u8, поэтому он хранит много элементов вместо одного.
  2. Он имеет Box<...> вместо &..., поэтому ему принадлежит то, что он ссылается на.

Создание бокса размещает его содержимое в куче и, таким образом, напрямую соответствует использованию malloc. Здесь я изменил ваш код, чтобы показать, как это работает, а также позаботился обо всех предупреждениях:

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]);
}

Обратите внимание, что явные параметры времени жизни <'a> и 'static исчезли; они сейчас не нужны, и они бы не работали (потому что что-то, выделенное функцией , не может быть 'static, если вы не выберете утечку это).

Однако для этого по-прежнему требуется const ширина и высота, чтобы построить массив, который не будет работать, как только ширина и высота будут считаны из файла карты. Чтобы исправить это, вам нужно построить массив (или, точнее, срез массива) другим способом; один из вариантов - использовать конструктор Vec (а затем упростить его до фрагмента массива, потому что вы не планируете изменять длину):

    data: vec![0; w * h].into_boxed_slice(),
...