【Rust】配列の基本と便利なメソッドの使い方

今回はRustにおける配列(Vector)について見ていきたいと思います。

Rust特有のものはOptionやResultに比べるとそんなにないので身構える必要はありません。

今までと同様、自身の勉強のアウトプット・整理と同時に、同じようにRustを勉強している方や「公式ドキュメントはちょっと、、」というような方が気楽に見れるようにサンプルコードをメインに簡単にまとめてみました。

この記事でできること
  • 配列型の基本
  • 配列型の便利なメソッドとその使い方
目次

基本

Rustにおける配列は以下の3種類があります。

  • 配列
  • スライス
  • Vector

配列

Rustにおける基本データ型の配列です。

以下の特徴を持ちます。

  • 固定長の静的な配列
  • 宣言後に要素数を増減させることはできない
  • 変数をmutにすることで再代入は可能

サンプルコード

fn sample() {
    let ary: [i32; 5] = [1, 2, 3, 4, 5]; // 基本の配列宣言
    println!("{:?}", ary);
    // -> [1, 2, 3, 4, 5]

    let ary2 = [3; 5]; // 要素数5で、すべての要素を3にする
    println!("{:?}", ary2);
    // -> [3, 3, 3, 3, 3]

    let mut mut_ary = [1, 2, 3, 4, 5];
    mut_ary = [1]; // サイズ(型)の異なる配列で再代入することはできない
    mut_ary = [100, 200, 300, 400, 500]; // サイズ(型)が同じであれば再代入が可能
    println!("{:?}", mut_ary);
    // -> [100, 200, 300, 400, 500]

    mut_ary[1] = 2; // 一部のみを書き換えることも可能
    println!("{:?}", mut_ary);
    // -> [100, 2, 300, 400, 500]
}

Vector

Vectorは標準ライブラリに定義されているコレクション型です。

以下の特徴を持ちます。

  • 可変長の動的な配列
  • 宣言後に要素数を増減させることができる
  • 要素数の増減など対象変数自身の変更にはmutが必要

サンプルコード

fn sample() {
    let mut vector: Vec<i32> = Vec::new();
    vector.push(1);
    println!("{:?}", vector);
    // -> [1]

    let mut vec = vec![1, 2, 3];
    vec.push(100);
    println!("{:?}", vec);
    // -> [1, 2, 3, 100]

    let immutable_vec = vec![100, 200, 300];
    immutable_vec.push(1); // mutではないのでこの操作はできない
}
参考リンク

スライス

スライスは配列への参照のことを指します。

以下の特徴を持ちます。

  • 所有権がない
  • 配列やVectorへの参照
  • mutにすることで参照先を変えることは可能
  • 可変参照を用いることで参照先の中身をスライス経由で書き換えることも可能

サンプルコード

fn sample() {
    let ary = [1, 2, 3, 4, 5];
    let slice = &ary; // 配列全体へのスライス(=参照)
    println!("{:?}", slice);
    // -> [1, 2, 3, 4, 5]

    let slice = &ary[2..4]; // 配列の一部へのスライス(=参照)
    println!("{:?}", slice);
    // -> [3, 4]

    let vector = vec![100, 200, 300, 400, 500];
    let slice = &vector; // Vector全体へのスライス(=参照)
    println!("{:?}", slice);
    // -> [100, 200, 300, 400, 500]

    let slice = &vector[1..3]; // Vectorの一部へのスライス(=参照)
    println!("{:?}", slice);
    // -> [200, 300]
}

スライスにおけるmut時の挙動

変数をmutにした場合の挙動が気になったので色々と試してみました。

サンプルコード(参照先の変更)

fn sample() {
    // スライスがmutではない場合、参照先を変えることができない
    let slice = &[1, 2, 3, 4, 5];
    slice = &[6, 7, 8, 9, 10]; // できない
    
    // スライスがmutの場合、参照先を変えることができる
    let mut slice = &[1, 2, 3, 4, 5];
    println!("{:?}", slice); // -> [1, 2, 3, 4, 5]
    slice = &[6, 7, 8, 9, 10];
    println!("{:?}", slice); // -> [6, 7, 8, 9, 10]
}

サンプルコード(参照先の中身の変更)

fn sample() {
    let ary = [1, 2, 3, 4, 5];
    let mut mut_ary = [100, 200, 300, 400, 500];

    // 1. 参照先が不変(immutable)で、スライスが不変参照の場合
    let slice = &ary;
    slice[0] = 5; // 参照先の中身を変更することはできない

    // 2. 参照先が不変(immutable)で、スライスが可変参照の場合
    let slice = &mut ary; // 参照先がmutでないので可変参照が作れない
    slice[0] = 5; // 上記で可変参照を作れないので中身の変更もできない

    // 3. 参照先が可変(mutable)で、スライスが不変参照の場合
    let slice = &mut_ary;
    slice[0] = 5; // スライスが可変参照ではないので参照先の中身を書き換えることはできない

    // 4. 参照先が可変(mutable)で、スライスが可変参照の場合
    let slice = &mut mut_ary;
    slice[0] = 5; // 参照先の中身を書き換えることができる
}

配列の操作

要素へのアクセス(index, range)

配列要素へのアクセスは以下のように行います。

fn sample() {
    let ary = [1, 2, 3, 4, 5];
    println!("{:?}", ary[2]);
    // -> 3

    let vector = vec![100, 200, 300, 400, 500];
    println!("{:?}", vector[2]);
    // -> 300

    let slice = &ary;
    println!("{:?}", slice[2]);
    // -> 3

    // 参照(スライス)にすることで範囲指定が可能
    println!("{:?}", &ary[2..4]); // -> [3, 4]
    println!("{:?}", &vector[2..4]); // -> [300, 400]
}

ループ処理

基本的なループ処理は以下のように行います。

fn sample() {
    let ary = [1, 2, 3, 4, 5];
    for n in ary {
        println!("n={}", n);
        // -> n=1
        // -> n=2
        // -> n=3
        // -> n=4
        // -> n=5
    }

    let vector = vec![100, 200, 300, 400, 500];
    for n in vector {
        println!("n={}", n);
        // -> n=100
        // -> n=200
        // -> n=300
        // -> n=400
        // -> n=500
    }

    let slice = &ary[2..4];
    for n in slice {
        println!("n={}", n);
        // -> n=3
        // -> n=4
    }
}

他にもイテレータを使う方法や、配列に実装されているメソッドを使うことでループ処理する方法もありますが、それらについては次の章で記載します。

便利なメソッド(配列全般)

配列全般(配列、スライス、Vector)で使用できる便利なメソッドをいくつかピックアップします。

iter

iter

イテレータを配列への参照(&T)として取得する

fn sample() {
    let ary: [String; 5] = [
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];

    for value in ary.iter() {
        println!("{value}");
    }

    // 複数回イテレータを使用可能
    for value in ary.iter() {
        println!("{value}");
    }
}
into_iter

イテレータを配列の所有権を移動した状態(T)で取得する

fn sample() {
    let ary: [String; 5] = [
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];

    for value in ary.into_iter() {
        println!("{value}");
    }

    // 一度into_iterしたあとは所有権が移動しているためaryに対する操作はできない
    for value in ary.into_iter() {
        println!("{value}");
    }
}
iter_mut

イテレータを配列への可変参照(&mut T)として取得する

fn sample() {
    let mut ary: [String; 5] = [
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];

    for value in ary.iter_mut() {
        *value = String::from("100");
        println!("{value}");
    }

    // iterと同様に複数回イテレータを使用可能
    for value in ary.iter_mut() {
        println!("{value}");
    }
}
参考リンク

is_empty

要素がひとつもない場合にtrue、ひとつでもある場合はfalseを返す。

fn sample() {
    let mut vector: Vec<String> = vec![
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];

    println!("{:?}", vector.is_empty());
    // -> false

    vector.clear();
    println!("{:?}", vector.is_empty());
    // -> true
}

first, last

先頭の要素および末尾の要素を取得する。

fn sample() {
    let vector: Vec<String> = vec![
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];

    println!("{:?}", vector.first()); // -> Some("1")
    println!("{:?}", vector.last());  // -> Some("5")
}

get

指定された位置、範囲の要素を取得する。

fn sample() {
    let vector: Vec<String> = vec![
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];

    println!("{:?}", vector.get(2));    // -> Some("3")
    println!("{:?}", vector.get(2..4)); // -> Some(["3", "4"])
    println!("{:?}", vector.get(10));   // -> None
}

sort

要素をソートする。

fn sample() {
    let mut vector: Vec<String> = vec![
        String::from("5"),
        String::from("1"),
        String::from("3"),
        String::from("2"),
        String::from("4"),
    ];

    vector.sort();
    println!("{:?}", vector);
    // -> ["1", "2", "3", "4", "5"]
}

sort_by

要素を指定した関数の結果に応じてソートする。

関数の戻り地は列挙型Orderingとなる必要がある。

fn sample() {
    let mut vector: Vec<String> = vec![
        String::from("5"),
        String::from("4"),
        String::from("3"),
        String::from("2"),
        String::from("1"),
    ];

    // 関数の結果がOrdering::Greaterの場合は順序は入れ替わらない
    vector.sort_by(|a, b| Ordering::Greater);
    println!("{:?}", vector);
    // -> ["5", "4", "3", "2", "1"]

    // 関数の結果がOrdering::Equalの場合は順序は入れ替わらない
    vector.sort_by(|a, b| Ordering::Equal);
    println!("{:?}", vector);
    // -> ["5", "4", "3", "2", "1"]

    // 関数の結果がOrdering::Lessの場合は順序を入れ替える
    vector.sort_by(|a, b| Ordering::Less);
    println!("{:?}", vector);
    // -> ["1", "2", "3", "4", "5"]

    // 例:値の小さい順にソートする
    vector.sort_by(|a, b| a.partial_cmp(b).unwrap());
    println!("{:?}", vector);
    // -> ["1", "2", "3", "4", "5"]
}

reverse

要素の順序を反転する。

fn sample() {
    let mut vector: Vec<String> = vec![
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];

    vector.reverse();
    println!("{:?}", vector);
    // -> ["5", "4", "3", "2", "1"]
}

join

配列要素を指定された区切り文字で結合する。

fn sample() {
    let vector: Vec<String> = vec![
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];

    let str = vector.join(",");
    println!("{:?}", str);
    // -> "1,2,3,4,5"
}

contains

値がVectorの要素に含まれる場合はtrue、そうでない場合はfalseを返す。

fn sample() {
    let vector: Vec<String> = vec![
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];

    println!("{:?}", vector.contains(&String::from("1"))); // -> true
    println!("{:?}", vector.contains(&String::from("7"))); // -> false
}

便利なメソッド(Vector)

次はVectorで使用できるメソッドです。

truncate

Vectorの要素数を引数に指定した個数に削減する。

fn sample() {
    let mut vector: Vec<String> = vec![
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];

    vector.truncate(2); // 要素数を2個に削減する
    println!("{:?}", vector);
    // -> ["1", "2"]

    vector.truncate(6); // 保持している要素数よりも大きい数を指定した場合は削減はされない
    println!("{:?}", vector);
    // -> ["1", "2"]
}

insert

引数に指定した位置に要素を挿入する。

fn sample() {
    let mut vector: Vec<String> = vec![
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];

    vector.insert(2, String::from("insert"));
    println!("{:?}", vector);
    // -> ["1", "2", "insert", "3", "4", "5"]

    // 要素数よりも大きい位置を指定した場合はpanicとなる
    vector.insert(7, String::from("test"));
}

remove

引数に指定した位置の要素を削除する。

以下の特徴を持つ。

  • 削除後は残りの要素をシフトする
  • 要素の順番が保証される
  • シフト処理が発生するため速度は遅い(特にVectorのサイズが大きい場合)
fn sample() {
    let mut vector: Vec<String> = vec![
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];

    let removed = vector.remove(3);
    println!("{:?}", removed); // -> "4"
    println!("{:?}", vector); // -> ["1", "2", "3", "5"]

    // 要素数よりも大きい位置を指定した場合はpanicとなる
    vector.remove(6);
}

swap_remove

引数に指定した位置の要素を削除する。

以下の特徴を持つ。

  • 削除後、削除された要素のあった位置へVector内の最後の要素が移動する。
  • 要素の順番は保証されない。
  • シフト処理が発生しないためremoveと比べて高速。
fn sample() {
    let mut vector: Vec<String> = vec![
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];

    let removed = vector.swap_remove(1);
    println!("{:?}", removed); // -> "2"
    println!("{:?}", vector); // -> ["1", "5", "3", "4"]

    // 要素数よりも大きい位置を指定した場合はpanicとなる
    vector.swap_remove(6);
}

push

Vectorの末尾に要素を追加する。

fn sample() {
    let mut vector: Vec<String> = vec![
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];

    vector.push(String::from("6"));
    println!("{:?}", vector);
    // -> ["1", "2", "3", "4", "5", "6"]
}

pop

Vectorの末尾の要素を取り出す。

fn sample() {
    let mut vector: Vec<String> = vec![
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];
    println!("{:?}", vector.pop()); // -> Some("5")
    println!("{:?}", vector);       // -> ["1", "2", "3", "4"]

    let mut empty: Vec<i32> = vec![];
    println!("{:?}", empty.pop()); // -> None
    println!("{:?}", empty);       // -> []
}

retain

引数に指定した関数がtrueを返す要素のみ残す。

fn sample() {
    let mut vector: Vec<String> = vec![
        String::from("a"),
        String::from(""),
        String::from("ccc"),
        String::from(""),
        String::from("eeeee"),
    ];

    vector.retain(|x| !String::is_empty(x));
    println!("{:?}", vector); // -> ["a", "ccc", "eeeee"]
}

append

引数に指定したVectorの要素をメソッド実行元のVectorを移動する。

fn sample() {
    let mut vector: Vec<String> = vec![String::from("1"), String::from("2")];
    let mut vector2: Vec<String> = vec![String::from("3"), String::from("4"), String::from("5")];

    vector.append(&mut vector2);
    println!("{:?}", vector);  // -> ["1", "2", "3", "4", "5"]
    println!("{:?}", vector2); // -> []
}

drain

指定した範囲をVectorから削除して、削除した要素へのイテレータを返す。

fn sample() {
    let mut vector: Vec<String> = vec![
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];

    let iter = vector.drain(2..4);
    for v in iter {
        println!("{:?}", v);
        // -> "3"
        // -> "4"
    }

    println!("{:?}", vector);
    // -> ["1", "2", "5"]
}

clear

すべての要素を削除する。

fn sample() {
    let mut vector: Vec<String> = vec![
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];

    vector.clear();
    println!("{:?}", vector); // -> []
}

len / capacity

Vectorの長さ、容量を返す。

fn sample() {
    let mut vector: Vec<String> = vec![
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];

    println!("{:?}", vector.capacity()); // -> 5
    println!("{:?}", vector.len()); // -> 5

    // drainなどのメソッドはcapacityに影響を及ぼさないことに注意
    vector.drain(2..4);
    println!("{:?}", vector.capacity()); // -> 5
    println!("{:?}", vector.len()); // -> 3
}

extend

Vectorに別のVectorを追加する

fn sample() {
    let mut vector: Vec<String> = vec![
        String::from("1"),
        String::from("2"),
        String::from("3"),
        String::from("4"),
        String::from("5"),
    ];
    let another_vector: Vec<String> = vec![String::from("6"), String::from("7")];

    vector.extend(another_vector);
    println!("{:?}", vector);
    // -> ["1", "2", "3", "4", "5", "6", "7"]
}

最後に

配列関連の操作自体は他のプログラミング言語と大きく変わらないため難しいことはないです。

しかし、所有権まわりが絡んでくると少し複雑になってきますね。

ここらへんは様々なパターンを実装して試しながら覚えていくしかなさそうです。

よかったらシェアしてね!
  • URLをコピーしました!
目次