Rustのベクタ(Vec)を完全マスター!作り方から操作、注意点まで解説

2025年4月21日月曜日

Rust

Rustのベクタ(Vec)って聞いたことあるけど、配列と何が違うの?どうやって使うの?

そんな疑問で頭がいっぱいになっていませんか?

この記事では、Rustを始めたばかりの方でも迷わないように、ベクタの基本から、実際にプログラムで動かすためのコード例、ちょっとしたコツや気をつけるべき点まで、詳しく解説していきます。

この記事でわかること

  • ベクタがどんなものか、配列との違いがわかる
  • ベクタの作り方を覚えられる
  • ベクタのデータを読んだり、書き換えたりできるようになる
  • ベクタにデータを追加したり、削除したりする方法が身につく
  • ベクタの中身を順番に処理する方法をマスターできる
  • ベクタを使うときに気をつけるポイントが理解できる


Rustのベクタとは?配列との違いも理解しよう

まず、Rustのベクタ(`Vec<T>`)が何者なのか、ざっくり掴んでおきましょう!

一言でいうと、プログラム実行中にサイズ(要素数)を変えられるリストのようなものです。他のプログラミング言語でいう「動的配列」や「リスト」に近いイメージですね。

Rustには「配列」もありますが、こちらは一度作るとサイズを変えられません。例えば、最初に要素を5つ持つ配列を作ったら、ずっと要素数は5つのままです。

一方、ベクタはもっと柔軟!最初は空っぽでも、後からデータをどんどん追加できますし、逆に減らすことも可能です。データを一時的にたくさん溜め込みたい時や、いくつデータが必要になるか事前にわからない場合に、ベクタはとっても役立ちます。

なぜRustでベクタがよく使われるかというと、Rustがメモリを安全に管理する仕組み(所有権とか、ちょっと難しい話!)とも相性が良いからなんです。

まあ、今は「サイズが変えられる便利なリストなんだな」くらいに思っておけばOKです。

Rustのベクタの基本的な作り方(宣言と初期化)

では、実際にベクタを作ってみましょう!作り方は主に2パターンあります。どちらも簡単なので、サクッと覚えちゃいましょう。

空のベクタを作成する (`Vec::new`)

最初は中身が何もない、空っぽのベクタを作りたい場合がありますよね。そんなときは `Vec::new()` を使います。

ただし、この方法だと、ベクタにどんな種類のデータを入れるのか、Rustコンパイラが判断できません。なので、変数の型を明示的に教えてあげる必要があります。
(後で `push` などで要素を追加すれば、型が決まる場合もあります)

fn main() {
    // i32型(32ビット整数)の要素を入れる空のベクタを作成
    let mut v: Vec<i32> = Vec::new();

    // あとから要素を追加できる
    v.push(10);
    v.push(20);
    v.push(30);

    println!("vの中身: {:?}", v); // デバッグ出力
}

表示結果

vの中身: [10, 20, 30]

ここで `let mut v` のように `mut` を付けているのは、後で `v.push()` を使ってベクタの中身を変更する(mutableにする)ためです。`mut` がないと、要素を追加しようとしたときにエラーになりますよ。

初期値を持つベクタを作成する (`vec![]` マクロ)

最初からいくつかデータが入った状態のベクタを作りたいなら、`vec![]` マクロを使うのが断然ラクチンです!角括弧 `[]` の中に、入れたい要素をカンマ区切りで並べるだけ。

この方法なら、Rustコンパイラが入れた要素から型を推測してくれるので、型を明示的に書かなくても良い場合が多いです。便利!

fn main() {
    // 整数が入ったベクタを作成 (型はi32と推論される)
    let v1 = vec![1, 2, 3, 4, 5];
    println!("v1の中身: {:?}", v1);

    // 文字列スライスが入ったベクタを作成 (型は&strと推論される)
    let v2 = vec!["apple", "banana", "orange"];
    println!("v2の中身: {:?}", v2);

    // 要素が1つだけでもOK
    let v3 = vec![100];
    println!("v3の中身: {:?}", v3);

    // もちろん空のベクタも作れるけど、型が必要な場合がある
    // let v4 = vec![]; // これは型が不明でエラーになる可能性がある
    let v4: Vec<i32> = vec![]; // 型を指定すればOK
    println!("v4の中身: {:?}", v4);
}

表示結果

v1の中身: [1, 2, 3, 4, 5]
v2の中身: ["apple", "banana", "orange"]
v3の中身: [100]
v4の中身: []

どうです? `vec![]` マクロ、とっても簡単でしょ?普段はこちらを使うことが多いかもしれませんね。

Rustのベクタの要素にアクセス・変更する方法

ベクタを作ったら、次はその中身を見たり、書き換えたりしたくなりますよね。要素へのアクセスも簡単です!

一番基本的なのは、配列と同じようにインデックス(番号)を使う方法です。インデックスは `0` から始まることに注意してくださいね。ベクタ `v` の最初の要素は `v[0]`、2番目は `v[1]` という具合です。

fn main() {
    let mut v = vec![10, 20, 30, 40, 50];

    // インデックスを使って要素にアクセス (読み取り)
    let first_element = v[0]; // 最初の要素 (10)
    let third_element = v[2]; // 3番目の要素 (30)
    println!("最初の要素: {}", first_element);
    println!("3番目の要素: {}", third_element);

    // インデックスを使って要素を変更
    v[1] = 25; // 2番目の要素を20から25に変更
    println!("変更後のv: {:?}", v);

    // 範囲外のインデックスにアクセスしようとすると…?
    // let sixth_element = v[5]; // これはエラー (panic!) になります!
    // println!("6番目の要素: {}", sixth_element);
}

表示結果

最初の要素: 10
3番目の要素: 30
変更後のv: [10, 25, 30, 40, 50]

上のコードの最後でコメントアウトしている部分のように、ベクタの範囲外のインデックス(例: 要素数が5なのに `v[5]` にアクセス)を指定すると、プログラムが `panic` というエラーを起こして強制終了してしまいます。これは結構ありがちなミスなので気をつけましょう!

じゃあ、安全にアクセスするにはどうすればいいの? という疑問には、`get()` メソッドが答えてくれます。

`get()` はインデックスを引数に取り、その位置に要素があれば `Some(要素の値)` を、なければ `None` を返します。

これは `Option` という型で、「値があるかもしれないし、ないかもしれない」状態を表します。`panic` する代わりに、値があるかどうかをチェックできるので安全ですね。

fn main() {
    let v = vec![10, 20, 30];

    // get() を使って安全にアクセス
    let second = v.get(1); // インデックス1 (2番目の要素) を取得
    match second {
        Some(value) => println!("2番目の要素は: {}", value),
        None => println!("2番目の要素はありません。"),
    }

    let fifth = v.get(4); // 範囲外のインデックス4を取得
    match fifth {
        Some(value) => println!("5番目の要素は: {}", value),
        None => println!("5番目の要素はありません。"),
    }

    // if let を使うともっと簡潔に書けることも
    if let Some(value) = v.get(0) {
        println!("if let で取得: 最初の要素は {}", value);
    }
}

表示結果

2番目の要素は: 20
5番目の要素はありません。
if let で取得: 最初の要素は 10

`[]` で直接アクセスするか、`get()` で安全にアクセスするかは、状況に応じて使い分けるのが良いでしょう。

Rustのベクタに要素を追加・削除する方法

ベクタの真骨頂は、なんといっても要素を自由に追加したり削除したりできること!そのための基本的なメソッドを見ていきましょう。

要素を末尾に追加する (`push`)

ベクタの一番最後に新しい要素を追加したいときは、`push` メソッドを使います。これは非常によく使う操作ですよ!

`push` を使うには、ベクタが `mut` (変更可能) である必要があります。

fn main() {
    let mut v = vec![1]; // 最初に要素1だけを持つベクタ
    println!("push前: {:?}", v);

    v.push(2); // 末尾に2を追加
    println!("push後 (2追加): {:?}", v);

    v.push(3); // 末尾に3を追加
    println!("push後 (3追加): {:?}", v);
}

表示結果

push前: [1]
push後 (2追加): [1, 2]
push後 (3追加): [1, 2, 3]

ね、簡単でしょ? `push` でどんどん要素を増やせます。

要素を末尾から削除する (`pop`)

逆に、ベクタの一番最後の要素を取り除きたい(削除したい)場合は、`pop` メソッドを使います。

`pop` もベクタを変更するので、ベクタは `mut` である必要があります。
`pop` は、取り除いた要素を `Option` 型で返します。ベクタが空でなければ `Some(最後の要素)` を、空なら `None` を返します。

fn main() {
    let mut v = vec![10, 20, 30];
    println!("pop前: {:?}", v);

    let last_element = v.pop(); // 末尾の要素 (30) を削除して取得
    println!("popで取得した要素: {:?}", last_element); // Some(30) が表示されるはず
    println!("pop後 (1回目): {:?}", v);

    let second_last_element = v.pop(); // 末尾の要素 (20) を削除して取得
    println!("popで取得した要素: {:?}", second_last_element); // Some(20)
    println!("pop後 (2回目): {:?}", v);

    v.pop(); // 末尾の要素 (10) を削除
    println!("pop後 (3回目): {:?}", v); // これで空になる

    let empty_element = v.pop(); // 空のベクタに対してpop
    println!("空のベクタからpop: {:?}", empty_element); // None が表示されるはず
}

表示結果

pop前: [10, 20, 30]
popで取得した要素: Some(30)
pop後 (1回目): [10, 20]
popで取得した要素: Some(20)
pop後 (2回目): [10]
pop後 (3回目): []
空のベクタからpop: None

`push` と `pop` はセットで覚えておくと便利ですよ!

ちなみに、特定の位置に要素を挿入する `insert()` や、特定の位置の要素を削除する `remove()` というメソッドもありますが、まずは `push` と `pop` をしっかりマスターしましょう。

Rustのベクタを繰り返し処理 (イテレーション) する方法

ベクタの中身を一つずつ順番に取り出して何か処理をしたい、ということはよくあります。

例えば、ベクタの全要素を画面に表示したり、合計値を計算したり。このような繰り返し処理を「イテレーション」と呼びます。

Rustでは `for` ループを使うのが一般的です。ベクタのイテレーションには、主に3つの方法があります。

1. 要素の参照 (`&T`) を取得する (`iter()` を使う)
元のベクタや要素の値を変更せず、ただ読み取りたいだけの場合に使います。これが一番安全でよく使われる方法かもしれません。

fn main() {
    let v = vec!["a", "b", "c"];

    // iter() を使って各要素への参照を取得
    for element in v.iter() {
        println!("参照: {}", element);
        // *element = "z"; // ここで変更しようとするとエラー!参照は変更不可
    }

    // ループの後も v は使える
    println!("ループ後のv: {:?}", v);
}

表示結果

参照: a
参照: b
参照: c
ループ後のv: ["a", "b", "c"]

2. 要素のミュータブルな参照 (`&mut T`) を取得する (`iter_mut()` を使う)
ループの中で要素の値を変更したい場合に使います。ベクタ自体が `mut` である必要があります。

fn main() {
    let mut v = vec![100, 200, 300];

    // iter_mut() を使って各要素へのミュータブルな参照を取得
    for element in v.iter_mut() {
        *element += 10; // 参照先の値を変更 (* が必要)
        println!("変更中: {}", element);
    }

    println!("ループ後のv: {:?}", v);
}

表示結果

変更中: 110
変更中: 210
変更中: 320
ループ後のv: [110, 210, 320]

`*element` のようにアスタリスク `*` を付けて、参照先の値にアクセスして変更していますね。

3. 要素の所有権を移動する (`into_iter()` または直接 `for elem in v`)
ループの中で各要素の所有権を完全に取得したい場合に使います。この方法を使うと、ループが終わった後、元のベクタ `v` は使えなくなってしまうので注意が必要です!

fn main() {
    let v = vec![String::from("hello"), String::from("world")];

    // ベクタ v の所有権がループに移る
    for element in v { // .into_iter() は暗黙的に呼ばれることが多い
        println!("所有権取得: {}", element);
        // ここで element (String) を自由にいじれる
    }

    // ループの後、v はもう使えない!
    // println!("ループ後のv: {:?}", v); // これはコンパイルエラー!
}

表示結果

所有権取得: hello
所有権取得: world

どのイテレーション方法を使うかは、ループの中で要素をどう扱いたいか(読むだけ?変更したい?所有したい?)によって決まります。最初は `iter()` から試してみるのが良いかもしれませんね。

Rustのベクタを使う上での注意点

ベクタはとても便利ですが、いくつか気をつけておきたい点があります。これを知っておくと、予期せぬエラーを防いだり、よりスムーズにプログラミングを進めたりできますよ。

範囲外アクセスに注意!

これは先ほども触れましたが、`v[index]` の形で要素にアクセスするとき、`index` がベクタの範囲を超えているとプログラムが `panic` します。

例えば、要素数が3(インデックスは0, 1, 2)のベクタに対して `v[3]` とアクセスするのはNGです。心配な場合は `v.get(index)` を使って、値があるかどうかを確認する習慣をつけると安全です。

所有権と借用ルールを意識しよう

Rustには「所有権」や「借用」という、メモリ安全性を保つための独特なルールがあります。ベクタを使うときも、これらのルールが関係してきます。

例えば、あるベクタへの「変更可能な参照(`&mut T`)」を持っている間は、同じベクタへの他の参照(変更可能でも不可能でも)を持つことができません。`for` ループで `iter_mut()` を使っている最中に、そのベクタに `push` しようとしたりすると、コンパイラに怒られることがあります。

また、ベクタを単純に関数に渡すと、所有権が関数に移ってしまい、元の場所では使えなくなることがあります。これを避けたい場合は、「参照(`&Vec` や `&[T]` スライス)」を渡すのが一般的です。

最初は難しく感じるかもしれませんが、「コンパイラのエラーメッセージをよく読む」ことを心がけていれば、徐々に慣れていきますよ!

要素追加時のメモリ再確保(ちょっとだけ気にする程度でOK)

ベクタはサイズが可変ですが、内部的にはある程度のメモリ領域をあらかじめ確保しています。`push` で要素を追加していき、その確保した領域がいっぱいになると、ベクタはもっと大きな新しいメモリ領域を探し、そこに今までの全要素をコピーしてから、新しい要素を追加します。

この「再確保」の処理は、ほんの少しだけ時間がかかることがあります。ものすごくパフォーマンスが求められる場面以外では、ほとんど気にする必要はありません。

ただ、もし最初から「だいたいこれくらいの要素数になりそうだな」と予想がつく場合は、`Vec::with_capacity(予想数)` という関数で、最初に十分な領域を確保しておくと、途中の再確保を減らせて少し効率が良くなる、なんてテクニックもあります。

これらの注意点は、使いながら徐々に体験していくのが一番です。怖がらずに、どんどんベクタを使ってみてください!

【まとめ】Rustのベクタを使いこなして開発を加速しよう!

お疲れ様でした!この記事では、Rustのパワフルなコレクション型である「ベクタ(`Vec<T>`)」について、基本から応用まで一通り見てきましたね。

ポイントを振り返ってみましょう。

  • ベクタはサイズ(要素数)を自由に変えられるリスト。
  • `Vec::new()` や `vec![]` で簡単に作れる。
  • インデックス `[]` や `get()` で要素にアクセス・変更できる。
  • `push` で末尾に要素を追加、`pop` で末尾から削除できる。
  • `for` ループと `iter()`, `iter_mut()`, `into_iter()` で繰り返し処理ができる。
  • 範囲外アクセスや所有権ルールには少し注意が必要。

ベクタは、Rustプログラミングの様々な場面で登場するとても基本的なデータ構造です。ファイルから読み込んだデータを保持したり、ユーザーの入力を一時的に保存したり、計算結果をまとめたり…使いこなせれば、あなたの書くプログラムの表現力が格段にアップするはず!

今回学んだことを土台にして、ぜひ実際のコードでベクタを使ってみてください。もしわからないことが出てきても、公式ドキュメント(The Rust Programming Language、通称 The Book)などを参考にすれば、きっと解決策が見つかります。

【関連記事】
Rustとは?いま学ぶべき理由と特徴や始め方を詳しく解説

このブログを検索

  • ()

自己紹介

自分の写真
リモートワークでエンジニア兼Webディレクターとして活動しています。プログラミングやAIなど、日々の業務や学びの中で得た知識や気づきをわかりやすく発信し、これからITスキルを身につけたい人にも役立つ情報をお届けします。 note → https://note.com/yurufuri X → https://x.com/mnao111

QooQ