ちょっと前からRustを触り始めて、ようやく多少動かせるようになってきた。 で今までわかった範囲で、配列の初期化についてまとめてみた。
リテラル
Rustで配列を記述するには、型を [要素の型; 要素数]
という具合に指定する。
で初期化の要素をブラケット内 [ ]
に記述する:
let array: [i32; 3] = [1, 2, 3]; // 1, 2, 3 のi32型3要素の配列 |
デフォルト値で埋める
要素数が多い場合、初期化時にすべての要素の初期値を列挙するのが大変。
デフォルト値で初期化したい場合、要素数がそこまで多くなければ Default::default()
というのが使える:
let array2: [i32; 32] = Default::default(); // 0が32個 |
使用できる個数に制限があって、 32個以下 でしか使えない。 なんでそんな制約があるんだと思うんだけど、マクロで実装されているからなのかもしれない。
- Rust 1.47 のconst generics で32以上も可能になるとのこと
一定の値で埋める
要素の型が Copy
トレイトを実装している場合、全要素を同じ値で初期化するなら [要素; 要素数]
と書ける:
let array3: [i32; 33] = [1; 33]; |
この場合には32個以上も可能。
個別に初期化したい場合
リテラルでの直接記述や同じ値での初期化じゃなく、インデクスを使ってとか乱数を使ってとかで初期化したい場合どうするか。 Rustは安全性が最優先の言語で、変数が未初期化の状態を許さないので、でかい配列といえど確保のみということはできず、要素もすべて宣言時に初期化済みの状態になっていないといけない。
ではどうするか。
実のところ、 unsafe
を使うことになる:
use std::mem::MaybeUninit; |
MaybeUninit
で要素が未初期化の状態の配列を確保し、
各要素を初期化した後、
std::mem::transmute
で型を変換して取り出す。
「え、そんな基本的なことで unsafe
を使わないといけないの?」と思うのだけど、そういうものらしい。
crate を利用する
上の MaybeUninit
を使う方法は決まった形式なので、モジュール化できれば簡潔に利用できて便利。
crates.io にいろいろあるみたいなんだけど、 array-macro というものを使ってみた。
Cargo.toml の [dependencies]
に追加:
[dependencies] |
して、利用
use array_macro::*; |
できる。
クロージャを渡す方法以外にも通常の固定値での初期化と同じようにも書けるが、こちらは Copy
トレイトじゃない型でも使用できるという違いがある。
静的な配列の個数指定
静的な配列も定義できる。 ローカル変数の場合は型宣言を省略できるが、静的な場合には明示する必要がある:
pub const ARRAY: [i32; 3] = [1, 2, 3]; |
開発時にコードの修正で要素数を増減させる場合に、個数宣言(; 3
の箇所)も合わせないといけなくて要素数に応じて修正する必要があるのが地味に面倒だ。
でcrateがないか調べたらあった:counted-array (from Array length inference?)。
これを使えば個数に _
を指定できて、自動的にサイズを埋めてくれる:
counted_array!(pub const ARRAY2: [i32; _] = [1, 2, 3]); |
雑感
コアな言語仕様だけ用意してマクロでなんとかするというのも理にかなっているといえばそうなんだけど、 「このcrateを使えば簡単にできますよ」というのは言語に慣れてない人間にとっては探すのも難しいのでなかなか辛いと思った。