Vecとは何か
Rustで「可変長の配列」を扱いたいとき、最もよく使うのが Vec<T>(ベクター)です。T は格納する要素の型を表しており、同じ型のデータをまとめて管理できます。
固定長の配列([T; N])とは異なり、Vec<T> は実行時に要素を追加・削除できるため、要素数が事前にわからない場面で非常に便利です。
Vecの生成方法
vec!マクロを使う
最もシンプルな生成方法は vec! マクロです。
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
println!("{:?}", numbers); // [1, 2, 3, 4, 5]
}
Vec::newで空のVecを作る
空の Vec を作って後から要素を追加する方法もあります。この場合、変数に mut をつけて可変にする必要があります。
fn main() {
let mut fruits: Vec<String> = Vec::new();
fruits.push(String::from("apple"));
fruits.push(String::from("banana"));
fruits.push(String::from("cherry"));
println!("{:?}", fruits); // ["apple", "banana", "cherry"]
}
Vec::with_capacityで容量を予約する
要素数がある程度わかっている場合は、with_capacity で初期容量を指定するとメモリ再確保の回数を減らせてパフォーマンスが向上します。
fn main() {
let mut v = Vec::with_capacity(10);
for i in 0..10 {
v.push(i);
}
println!("len: {}, capacity: {}", v.len(), v.capacity());
// len: 10, capacity: 10
}
要素のアクセスと変更
インデックスアクセスとgetメソッド
インデックスで直接アクセスする方法と、get メソッドで Option<&T> を受け取る方法があります。
fn main() {
let v = vec![10, 20, 30];
// インデックスアクセス(範囲外はパニック)
println!("{}", v[1]); // 20
// getメソッド(範囲外はNone)
match v.get(5) {
Some(val) => println!("値: {}", val),
None => println!("インデックスが範囲外です"),
}
}
実運用では、存在しないインデックスへのアクセスでプログラムがクラッシュしないよう、get メソッドを使う習慣をつけると安全です。
要素の削除
pop は末尾の要素を取り出し、remove は指定インデックスの要素を削除します。
fn main() {
let mut v = vec![1, 2, 3, 4, 5];
let last = v.pop(); // Some(5)
println!("{:?}", last); // Some(5)
println!("{:?}", v); // [1, 2, 3, 4]
v.remove(1); // インデックス1の要素(2)を削除
println!("{:?}", v); // [1, 3, 4]
}
スライスとしての利用
Vec<T> はスライス &[T] として参照できます。関数の引数にスライスを受け取るようにすると、Vec だけでなく固定長配列も渡せる柔軟な設計になります。
fn sum(values: &[i32]) -> i32 {
let mut total = 0;
for &v in values {
total += v;
}
total
}
fn main() {
let v = vec![1, 2, 3, 4, 5];
println!("合計: {}", sum(&v)); // 合計: 15
let arr = [10, 20, 30];
println!("合計: {}", sum(&arr)); // 合計: 60
}
イテレータを使った変換
Rustのイテレータはとても強力です。map・filter・collect を組み合わせることで、ループを書かずに簡潔に変換処理を記述できます。
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6];
// 偶数だけ取り出して2倍にする
let result: Vec<i32> = numbers
.iter()
.filter(|&&x| x % 2 == 0)
.map(|&x| x * 2)
.collect();
println!("{:?}", result); // [4, 8, 12]
}
into_iter と iter の違い
| メソッド | 動作 | 元のVec |
|---|---|---|
iter() | 不変参照でイテレート | 所有権を保持 |
iter_mut() | 可変参照でイテレート | 所有権を保持 |
into_iter() | 所有権ごと移動してイテレート | 消費される |
fn main() {
let v = vec![1, 2, 3];
// into_iter: 所有権を消費してStringに変換
let strings: Vec<String> = v.into_iter()
.map(|x| x.to_string())
.collect();
println!("{:?}", strings); // ["1", "2", "3"]
// ここでvは使えない(所有権が移動したため)
}
よく使うメソッド一覧
最後に、覚えておくと便利なメソッドをまとめます。
fn main() {
let mut v = vec![3, 1, 4, 1, 5, 9, 2, 6];
println!("長さ: {}", v.len()); // 8
println!("空か: {}", v.is_empty()); // false
v.sort();
println!("ソート後: {:?}", v); // [1, 1, 2, 3, 4, 5, 6, 9]
v.dedup();
println!("重複削除後: {:?}", v); // [1, 2, 3, 4, 5, 6, 9]
v.retain(|&x| x % 2 != 0);
println!("奇数のみ: {:?}", v); // [1, 3, 5, 9]
println!("含まれるか: {}", v.contains(&3)); // true
}
まとめ
Vec<T> はRustで最もよく使うコレクション型です。今回の内容を振り返ります。
vec!マクロやVec::new()で生成できるpush/pop/removeで要素を操作できるgetメソッドを使うと安全に要素にアクセスできる- スライス
&[T]として関数に渡すと柔軟な設計になる iter()+map/filter/collectで宣言的に変換できる
Vec<T> をしっかり使いこなすことで、Rustのコレクション操作全般への理解が深まります。次は HashMap や BTreeMap など他のコレクション型にも挑戦してみてください!