はじめに

みなさん、Rustしてますか? Rustの配列って使い勝手が悪いですよね。
例えばStringの配列を作ろうとして、コンパイルが通らない経験とかあると思います。
そんな、配列の初期化方法について踏み込んだ記事が見つからなかったので、まとめてみました。
配列の使いにくさに関する解説記事も書けたら書きたいな…。

配列の代わりにVecを使う

前提を覆すようですが、『Rustで配列を使え』は全て詐欺です。Vecを使いましょう。
Vecは配列の上位互換です。配列で出来ることはVecでも出来ますし、オーバヘッドが問題となることも普通は無いはずです。Vecを使いましょう。
Vecの初期化方法について、例を3つだけ示しておきます。ここで語る内容でもない気がする…。

    // マクロで初期化
    let a = vec!["a".to_string(); 10];
    // pushで初期化
    let mut b = Vec::with_capacity(10);
    b.push(1);
    b.push(2);
    b.push(3);
    // イテレータで初期化
    let c: Vec<i32> = (0..100).collect();

Vecから生成する

配列の初期化方法で最も重要なのが、Vecからの変換です。これ一つだけ覚えておけば大抵は対応できます。
Vecの大きさがNでない場合は、unwrapで落ちます。

    use std::convert::TryInto;
    // 1.48.0 以降
    let output: [T; N] = input.try_into().unwrap();

スタック領域を介さずに直接Boxで包んで生成することも可能です。

    use std::convert::TryInto;
    // 1.43.0 以降
    let output: Box<[T; N]> = input.into_boxed_slice().try_into().unwrap();

イテレータから生成する

イテレータ→Vec→配列のように、一旦Vecに変換します。特に新しいことはありません。

    use std::convert::TryInto;
    let tmp: Vec<T> = input.take(N).collect();
    // 1.48.0 以降
    let output: [T; N] = tmp.try_into().unwrap();

Boxの場合

    use std::convert::TryInto;
    let tmp: Box<[T]> = input.take(N).collect();
    // 1.43.0 以降
    let output: Box<[T; N]> = tmp.try_into().unwrap();

コンパイル時定数から生成する

1.38でうっかり有効化され、1.50で事後承認された曰く付きの機能だそうです。

    //
    const INIT_VEC: Vec<T> = Vec::new();
    let data_bin = [INIT_VEC; N];
    //
    const INIT_VAL: Option<T> = None;
    let lazy_array = [INIT_VAL; N];
    //
    let str_array = {
        const INIT_VAL: String = String::new();
        [INIT_VAL; N]
    };

 

窓として参照を生成する

配列の実体が本当に必要でしたか? 参照だけで良い場合、長い配列やVecの一部を窓のように切り取ってアクセスすることが出来ます。

    use std::convert::TryInto;
    let tmp: &mut [T] = &mut input[0..N];
    // 1.34.0 以降
    let output: &mut [T; N] = tmp.try_into().unwrap_or_else(|_| unreachable!());

まとめ

    • 『Rustで配列を使え』は全て詐欺。代わりにVecを使う。

 

    • 配列はVecを変換して生成する。

 

    『Rustで配列を使え』は(ry
bannerAds