0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Rustでベクタの配列(array of vec)を作りたかった

Last updated at Posted at 2022-10-19

概要

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

やりたいこと

main.rs
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<Point>
    • 点群データを管理したい -> 要素数が未知のため、ベクタで確保
  • そのベクタを要素とする要素数100の配列
    • 座標により、100つの格子分けをしたかった
    • 格子別に処理を行う際、配列に確保されていれば便利だと考えた

解決策

1 : Default::default()を使う

main.rs
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 で宣言する (非推奨?)

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

main.rs
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<Point>)、要素数 (usize) をconstであらかじめ宣言しておき、それを使う。
ただしこちらもバージョンによって動作しないらしい。

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

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

main.rs
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 で作ってみた。
手順としては、

  1. std::mem::MaybeUninit<T>を使い、配列を初期化なしで宣言する。C で言うint array[10];みたいなもの
  2. その配列を for ループで回し、各要素に初期価値を代入する
  3. std::mem::transmuteを使い、std::mem::MaybeUninit<T><T> の配列に変換する

という風に、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/

0
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?