Rust でベクター型 (Vec) を扱う

Vec 型とは?

Rust のベクター型 (Vec<T>) は、特定の型 (T) の要素を保持する可変長配列です。 サイズが固定であれば、通常の配列 (array) を使った方が効率的です。

次の例では、Vec::new 関数で空の Vec<i32> インスタンスを作成し、push メソッドにより動的に要素を追加しています。

let mut v: Vec<i32> = Vec::new(); // 型は推論されるので省略可能
v.push(10);
v.push(20);
v.push(30);
println!("{:?}", v); //=> [10, 20, 30]

動的に要素を追加/削除するためには、Vec 変数は mut を付けて定義しておく必要があります。 この例の場合、push メソッドの使い方から、要素の型は i32 であることが推測されるので、Vec インスタンスの作成時に型注釈を省略することができます(ほとんどのケースでは省略できます)。

let mut v = Vec::new();

Vec インスタンスの作成方法

// 空の Vec を作成する
let mut v: Vec<i32> = Vec::new();

// vec! マクロで初期値を指定して作成する
let mut v: Vec<i32> = vec![];
let mut v = vec![10, 20, 30];
let mut v = vec![0; 3];  //=> [0, 0, 0]

// Range を collect して連番の Vec を作成する
let mut v: Vec<i32> = (0..3).collect();        //=> [0, 1, 2]
let mut v: Vec<i32> = (0..=3).collect();       //=> [0, 1, 2, 3]
let mut v: Vec<i32> = (0..=3).rev().collect(); //=> [3, 2, 1, 0]

Vec の要素を参照する

[] で参照する

[] 演算子により、指定したインデックスの要素を取得することができます。 不正なインデックスを指定すると、panic が発生してプログラムが強制終了します。

let v = vec!['A', 'B', 'C'];
let ch1 = v[0]; // 'A'
let ch2 = v[1]; // 'B'
let ch3 = v[2]; // 'C'
let ch4 = v[3]; // panic が発生!

get で参照する

get メソッドを使うと、Option 型 で要素を取得することができます。 引数で正しいインデックスを指定すると、要素をデータとして持つ Some バリアントが返され、不正なインデックスを指定すると、None バリアントが返されます。 戻り値を matchif let でハンドルすることで、安全に要素を取得することができます。

let v = vec!['A', 'B', 'C'];

// if let を使う方法
if let Some(elem) = v.get(0) {
    println!("最初の要素は {} です", elem);
}

// match を使う方法
match v.get(10) {
    Some(elem) => println!("要素の値は {} です", elem),
    None => println!("不正なインデックスです"),
}

Vec 要素をループ処理する

for in ループで Vec に格納された要素を 1 つずつ取り出すことができます。 ループ時に所有権の移動を防ぐため、& を付けて不変参照(スライス)を取得します。

let v = vec!['A', 'B', 'C'];
for elem in &v { // v.iter() でも可
    // elem は &char 型の不変参照
    println!("{}", elem);
}

ループで各要素の可変参照を取得すると、要素の値を変更することができます。

let mut v = vec![100, 200, 300];
for elem in &mut v { // v.iter_mut() でも可
    // elem は &mut i32 型の可変参照
    *elem *= 2;
}
println!("{:?}", v); //=> [200, 400, 600]

enumerate と組み合わせると、インデックス番号を取得しながらループ処理できます。

let v = vec!['A', 'B', 'C'];
for (index, elem) in v.iter().enumerate() {
    println!("{}:{}", index, elem);
}

Vec 要素を追加/削除する

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

let mut v = vec![];
v.push(10);

末尾の要素を取り出して削除する (pop)

let mut v = vec!['A', 'B', 'C'];

// if let で 1 つ取り出す
if let Some(elem) = v.pop() {
    println!("{elem}");
}

// while let で全部取り出す
while let Some(elem) = v.pop() {
    println!("{elem}");
}

指定したインデックスの要素を削除する (remove)

let mut v = vec!['A', 'B', 'C'];
v.remove(1);
println!("{:?}", v); //=> ['A', 'C']

指定したインデックスの前に挿入する (insert)

let mut v = vec!['A', 'B', 'C'];
v.insert(1, 'X');
println!("{:?}", v); //=> ['A', 'X', 'B', 'C']

enum で複数の型の要素を保持する

Vec<T> 型には、1 種類の型 T の要素しか格納できませんが、列挙型 (enum) の要素を保持するようにすれば、実質的に複数の型の要素を保持することができます。 Rust の列挙型は、各バリアントが異なる型のデータを持つことができるからです。

// 列挙型のバリアントで異なる型のデータを保持するようにする
enum Cell {
    /// 整数値を保持するセル
    Int(i32),
    /// 浮動小数点数を保持するセル
    Float(f64),
    /// テキストを保持するセル
    Text(String),
}

// 各バリアントで必要なデータを保持して Vec 要素として格納する
let cells = vec![
    Cell::Int(7),
    Cell::Text(String::from("AAA")),
    Cell::Float(12.34),
];

// Vec の各要素(列挙型)をループ処理
for elem in &cells {
    match elem {
        Cell::Int(x) => println!("Int: {}", x),
        Cell::Float(x) => println!("Float: {}", x),
        Cell::Text(x) => println!("Text: {}", x),
    }
}

その他の Vec 操作

ソートする (sort)

let mut v = vec![3, 9, 1, 7, 5];
v.sort();
println!("{:?}", v); //=> [1, 2, 3, 4, 5]

逆順にする (reverse)

let mut v = vec!['A', 'B', 'C'];
v.reverse();
println!("{:?}", v); //=> ['C', 'B', 'A']

指定したインデックスの要素を入れ替える (swap)

let mut v = vec!['A', 'B', 'C'];
v.swap(1, 2);
println!("{:?}", v); //=> ['A', 'C', 'B']

連続する要素を 1 つにまとめる (dedup)

let mut v: Vec<i32> = vec![5, 5, 2, 2, 2, 1, 4, 2];
v.dedup();
println!("{:?}", v); //=> [5, 2, 1, 4, 2]

条件に一致する要素だけ残して削除する (retain)

3 の倍数だけ残す
let mut v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
v.retain(|x| x % 3 == 0);
println!("{:?}", v); //=> [3, 6, 9]

条件に一致する要素を抽出して別の Vec として取得する (filter)

3 の倍数を抽出して Vec を作る
let v1 = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
let v2: Vec<i32> = v1.into_iter().filter(|x| x % 3 == 0).collect();
println!("{:?}", v2); //=> [3, 6, 9]

ループしながら要素を編集する

Vec の各要素に 100 を足すいろいろな方法です。

for-in を使う方法(インデックスでループ)
let mut v = vec![1, 2, 3];
for i in 0..v.len() {
    v[i] += 100;
}
println!("{:?}", v); //=> [101, 102, 103]

各要素を参照するためだけにインデックスを使うのはかっこよくないので、次のように各要素をイテレートするとよいです。

for-in + &mut でループ処理
for x in &mut v {
    *x += 100;
}

iter_mut メソッドの戻り値 (IterMut) を使って次のように書くこともできます。

for-in + iter_mut でループ処理
for x in v.iter_mut() {
    *x += 100;
}

iter_mut で取得したイテレーターの for_each メソッドを使うと、クロージャで変換処理をコンパクトに記述できます。

iter_mut + for_each でループ処理
v.iter_mut().for_each(|x| *x += 100);

不変の Vec インスタンスの内容から別の Vec インスタンスを生成するには、次のようにします。

iter + map + collect で新規 Vec を生成
let v1: Vec<i32> = vec![1, 2, 3];
let v2: Vec<i32> = v1.iter().map(|x| x + 100).collect();