コンテンツにスキップ

Rustのベクタ

ベクタ(vector、Vec)は配列に似ていますが、実行中に大きくしたり小さくしたりできます。普段のRustのコードで「何かのリスト」が必要になったら、固定長の配列よりも Vec を使うことのほうがずっと多いでしょう。

fn main() {
let numbers: Vec<i32> = vec![1, 2, 3];
println!("{:?}", numbers);
}

vec![] はマクロ(! がついていますね)で、値の並びから Vec を作ってくれます。配列リテラルを書くのと同じような感覚で使えます。

▶ 実際に試してみよう

空の状態から始めて、あとから要素を足していくこともできます。

let mut numbers: Vec<i32> = Vec::new();

末尾に追加するには .push() を使います。ベクタを変更するので、mut をつける必要があります。

fn main() {
let mut numbers = Vec::new();
numbers.push(1);
numbers.push(2);
numbers.push(3);
println!("{:?}", numbers);
}

実行結果:

[1, 2, 3]
▶ 実際に試してみよう

配列と同じ角かっこの書き方です。

fn main() {
let fruits = vec!["りんご", "バナナ", "さくらんぼ"];
println!("{}", fruits[0]);
println!("要素数: {}", fruits.len());
}
▶ 実際に試してみよう

おかしなインデックスでもパニックしない、より安全な取り出し方がほしいときは .get() を使います。これは値の代わりに Option を返します(詳しくは Option のページで説明します)。

fn main() {
let fruits = vec!["りんご", "バナナ", "さくらんぼ"];
match fruits.get(10) {
Some(fruit) => println!("見つかりました: {fruit}"),
None => println!("そのインデックスに果物はありません"),
}
}
▶ 実際に試してみよう
fn main() {
let mut numbers = vec![1, 2, 3, 4, 5];
numbers.pop(); // 最後の要素を削除 -> [1, 2, 3, 4]
numbers.remove(0); // インデックスで削除 -> [2, 3, 4]
println!("{:?}", numbers);
}
▶ 実際に試してみよう
fn main() {
let numbers = vec![10, 20, 30];
for n in &numbers {
println!("{n}");
}
}
▶ 実際に試してみよう

numbers の前の & に注目してください。for n in &numbers と書くと、ベクタの所有権を奪うのではなく、各要素を借用します。& をつけないと、ベクタはループの中にムーブ(move)されてしまい、そのあと numbers を使えなくなります。ほとんどの場合、ほしいのは借用のほうです。

ジェネリクスについてひとこと

Section titled “ジェネリクスについてひとこと”

型が Vec<i32> と書かれていることに気づいたでしょうか。<i32> の部分は「i32 の値のベクタ」という意味です。このかっこを使った書き方をジェネリクス(generics)と呼びます。きちんとした説明はジェネリクスのページでします。今のところは、Vec<T> を「T 型の値を入れるベクタ」と読めば十分です。

空のベクタから始めます。1から10までの数をループして、偶数だけを push してください。最後に、ベクタとその要素数を表示しましょう。ヒント:n % 2 == 0 のとき、数 n は偶数です。

解答を見る
fn main() {
let mut evens = Vec::new();
for n in 1..=10 {
if n % 2 == 0 {
evens.push(n);
}
}
println!("{:?}", evens);
println!("偶数は{}個見つかりました", evens.len());
}

実行結果:

[2, 4, 6, 8, 10]
偶数は5個見つかりました
▶ 実際に試してみよう

型を一度も書いていないことに注目してください。Rustは push(n) の呼び出しを見て、evens が Vec<i32> だと判断してくれます。

ベクタに3枚のお皿を push します:"青"、"緑"、そして "赤"。.pop() で上から2枚取り、それぞれを表示してください。最後に、残っているものを表示します。ヒント:.pop() は Option を返すので、{:?} で表示します。

解答を見る
fn main() {
let mut plates = Vec::new();
plates.push("青");
plates.push("緑");
plates.push("赤");
println!("取った: {:?}", plates.pop());
println!("取った: {:?}", plates.pop());
println!("残り: {:?}", plates);
}

実行結果:

取った: Some("赤")
取った: Some("緑")
残り: ["青"]
▶ 実際に試してみよう

pop() は、積み重ねたお皿の一番上を取るように、いつも最後に push した要素を取り出します。ただの値ではなく Some(...) を返すのは、ベクタが空かもしれないからです。空のときは、クラッシュする代わりに None が返ってきます。

  • Vec<T> は伸び縮みできるリストです。vec![...] か Vec::new() で作ります。
  • .push() は末尾に追加し、.pop() は最後の要素を削除し、.remove(i) はインデックスを指定して削除します。
  • vec[i] はおかしなインデックスでパニックします。.get(i) は代わりに Option を返すので、パニックしません。
  • for item in &vec でループすると、ベクタを消費せずに要素を借用できます。

理解度チェック

1. numbers[10] のように、範囲外のインデックスで Vec にアクセスするとどうなりますか?

2. パニックの心配なくインデックスで値を取り出せるメソッドはどれですか?

3. for n in &numbers の & は何をしていますか?

スコア: 0 / 3