rs.nkmk.me

Rustで配列・Vecを任意のサイズ・値で初期化

Posted: | Tags: Rust, ベクタ

Rustで配列およびベクタVecを任意のサイズ・値で初期化する方法について説明する。

関連する公式ドキュメント・リファレンスは以下。

空の配列・ベクタを生成する方法については以下の記事を参照。

以下のサンプルコードでは、説明のため、型推論によって省略できるときも明示的に型注釈を記述している場合がある。

任意の値で配列・Vecを生成

配列は角括弧[]にカンマ区切りで要素を書く。型注釈(型アノテーション)は[T; N]Nは要素数)。省略した場合は推論される。

let a = [0, 10, 20];
assert_eq!(a, [0, 10, 20]);

let a: [i32; 3] = [0, 10, 20];
assert_eq!(a, [0, 10, 20]);

let a = [0_i32, 10, 20];
assert_eq!(a, [0, 10, 20]);

最後の例のように、要素の型を指定してもよい。

ベクタVecの生成にはvec!マクロを使う。配列と同じ角括弧・カンマ区切りの前にvec!を付ける。型注釈はVec<T>


let v = vec![0, 10, 20];
assert_eq!(v, [0, 10, 20]);

let v: Vec<i32> = vec![0, 10, 20];
assert_eq!(v, [0, 10, 20]);

let v = vec![0_i32, 10, 20];
assert_eq!(v, [0, 10, 20]);

すべて同じ値で配列・Vecを初期化

すべての要素を同じ値にして配列・Vecを初期化することもできる。

配列では[x; N]xは初期値)とする。

let a = [10; 3];
assert_eq!(a, [10, 10, 10]);

let a: [i32; 3] = [10; 3];
assert_eq!(a, [10, 10, 10]);

let a = [10_i32; 3];
assert_eq!(a, [10, 10, 10]);

vec!マクロでも同様の書き方ができる。

let v = vec![10; 3];
assert_eq!(v, [10, 10, 10]);

let v: Vec<i32> = vec![10; 3];
assert_eq!(v, [10, 10, 10]);

let v = vec![10_i32; 3];
assert_eq!(v, [10, 10, 10]);

注意点1: 配列は要素数を変数で指定できない

配列を[x; N]で初期化する場合、要素数Nはコンパイル時定数である必要があり、変数では指定できない。

例えば、「標準入力から受け取った値を変数に束縛し、それを要素数として配列を生成」というように、実行時に要素数を決めることはできない。

A fixed-size array, denoted [T; N], for the element type, T, and the non-negative compile-time constant size, N. array - Rust

let length = 3;
// let a = [10; length];
// error[E0435]: attempt to use a non-constant value in a constant
// ---------- help: consider using `const` instead of `let`: `const length`

定数なら指定可能。

const LENGTH: usize = 3;
let a = [10; LENGTH];
assert_eq!(a, [10, 10, 10]);

Vecは変数で要素数を指定できる。

let length = 3;
let v = vec![10; length];
assert_eq!(v, [10, 10, 10]);

注意点2: 配列はコピーできない型を初期値として指定できない

配列を[x; N]で初期化するとき、初期値xにコピーできない型(Copyトレイトが実装されていない型)を指定できない。

A repeat expression [x; N], which produces an array with N copies of x. The type of x must be Copy. array - Rust

例えば、文字列スライス&strは参照でありCopyトレイトが実装されているので指定できるが、Stringは指定できない。

let a: [&str; 3] = ["abc"; 3];
assert_eq!(a, ["abc", "abc", "abc"]);

// let a: [String; 3] = [String::from("abc"); 3];
// error[E0277]: the trait bound `std::string::String: std::marker::Copy` is not satisfied
// note: the `Copy` trait is required because this value will be copied for each element of the array

[x; N]ではなく、[x0, x1, x2, ...]のように指定するのであればコピーできない型を要素とする配列を生成可能。

let a: [String; 3] = [
    String::from("abc"),
    String::from("abc"),
    String::from("abc"),
];
assert_eq!(a, ["abc", "abc", "abc"]);

Vecvec![x; N]でコピーできない型も要素にできる。

let v: Vec<String> = vec![String::from("abc"); 3];
assert_eq!(v, ["abc", "abc", "abc"]);

なお、vec![x; N]は、xN-1回クローンしてから最後に移動してVecの要素に追加する。最後には移動するので、変数をそのまま指定すると所有権を失う。

let s = String::from("abc");
let v: Vec<String> = vec![s; 3];
assert_eq!(v, ["abc", "abc", "abc"]);
// assert_eq!(s, "abc");
// error[E0382]: borrow of moved value: `s`

当然ながら、クローンを指定すれば所有権はそのまま。

let s = String::from("abc");
let v: Vec<String> = vec![s.clone(); 3];
assert_eq!(v, ["abc", "abc", "abc"]);
assert_eq!(s, "abc");

二次元配列(配列の配列、VecのVec)

二次元配列(配列の配列)を生成するには、入れ子のような形で指定する。型注釈がなくても推論されるが、ここでは説明のために記述している。

let a: [[i32; 2]; 3] = [[1, 2], [3, 4], [5, 6]];
assert_eq!(a, [[1, 2], [3, 4], [5, 6]]);

let a: [[i32; 2]; 3] = [[10; 2]; 3];
assert_eq!(a, [[10, 10], [10, 10], [10, 10]]);

二次元VecVecVec)も同様。

let v: Vec<Vec<i32>> = vec![vec![1, 2], vec![3, 4], vec![5, 6]];
assert_eq!(v, [[1, 2], [3, 4], [5, 6]]);

let v: Vec<Vec<i32>> = vec![vec![10; 2]; 3];
assert_eq!(v, [[10, 10], [10, 10], [10, 10]]);

さらに多次元にする場合、さらに入れ子にしていけばよい。[x0, x1, ...]で初期化する例は省略するが、考え方は同じ。

let a: [[[i32; 2]; 3]; 2] = [[[10; 2]; 3]; 2];
assert_eq!(
    a,
    [[[10, 10], [10, 10], [10, 10]],
     [[10, 10], [10, 10], [10, 10]]]
);

let v: Vec<Vec<Vec<i32>>> = vec![vec![vec![10; 2]; 3]; 2];
assert_eq!(
    v,
    [[[10, 10], [10, 10], [10, 10]],
     [[10, 10], [10, 10], [10, 10]]]
);

関連カテゴリー

関連記事