概要

タイトル通り、ベクタを要素に持つ配列を作りたかったが、エラーに悩んだのでメモ。
rustは配列を宣言すると、初期化することが求められるが、その初期化がうまくいかなかった。

やりたいこと

struct  Point{
    x: f32, 
    y: f32, 
    z: f32, 
}

fn main() {
    let array: [Vec<Point>; 100] = [vec![]; 100];
    // Error : the trait bound `Vec<Point>: Copy` is not satisfied
}

Point構造体を確保するベクタを要素として、配列に確保したかった。

    • 座標情報を持つPoint構造体

x, y, zの座標情報を持つ

格納するベクタVec

点群データを管理したい -> 要素数が未知のため、ベクタで確保

そのベクタを要素とする要素数100の配列

座標により、100つの格子分けをしたかった
格子別に処理を行う際、配列に確保されていれば便利だと考えた

解決策

1 : Default::default()を使う

fn main() {
    let array: [Vec<Point>; 10] = Default::default();        
    // これはOK

    // let array: [Vec<Point>; 100] = Default::default();        
    // これはNG (要素数が32を超えるため)
}

手っ取り早く解決するならこれ。
ただし要素数が32を超えるとNGの模様。
他にはrustのバージョンによって動作しないらしい。
自分の場合、要素数を100にしたかったため、別の方法を探した。

2 : 要素、サイズを const で宣言する (非推奨?)

初期化はできるが要素の追加ができないため、ほぼ意味のないコードになっている

fn main() {
    const INIT_ELEMENT: Vec<Point> = vec![];
    const ARRAY_SIZE: usize = 100;
    // 初期化に使用するパラメータをあらかじめ const で宣言
    let array: [Vec<Point>; ARRAY_SIZE] = [INIT_ELEMENT; ARRAY_SIZE];
    // これでOK
}

要素数に縛られることなく初期化したいならこれ。
配列を初期化する際、要素の型 (ここではVec)、要素数 (usize) をconstであらかじめ宣言しておき、それを使う。
ただしこちらもバージョンによって動作しないらしい。

追記
constでvecを確保しているので、pushができない。何やってんだこのコード…

3 : unsafe を使い、初期化する

fn main() {    
    // 1
    const SIZE: usize = 100; 
    let mut uninit_array: [std::mem::MaybeUninit<Vec<Point>>; SIZE];
    // 変数が初期化されないことを宣言する

    uninit_array = unsafe{
        std::mem::MaybeUninit::uninit().assume_init()
    };
    // 配列分確保のみをする    
    // 2
    for element in &mut uninit_array{        
        unsafe{std::ptr::write(element.as_mut_ptr(), vec![])};
        // 各要素を vec![] で初期化
    }
    // 3
    let mut array = unsafe{std::mem::transmute::<_, [Vec<Point>; SIZE]>(uninit_array)};            
    // std::mem::MaybeUninit<Vec<Point>> 型から、Vec<Point> に変換する
}

どうやっても safe Rust では再現できない気がしたので、 unsafe Rust で作ってみた。
手順としては、

std::mem::MaybeUninitを使い、配列を初期化なしで宣言する。C で言うint array[10];みたいなもの

その配列を for ループで回し、各要素に初期価値を代入する

std::mem::transmuteを使い、std::mem::MaybeUninit を の配列に変換する

という風に、Cライクに配列を初期化することで対応した。
このようなことでもunsafeを使わないといけないので、メモリ管理を意識しないといけないと分かった。

参考

https://doc.rust-lang.org/std/default/trait.Default.html
https://doc.rust-lang.org/nightly/std/mem/union.MaybeUninit.html#initializing-an-array-element-by-element
https://www.joshmcguigan.com/blog/array-initialization-rust/

广告
将在 10 秒后关闭
bannerAds