今回は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"]
}
最後に
配列関連の操作自体は他のプログラミング言語と大きく変わらないため難しいことはないです。
しかし、所有権まわりが絡んでくると少し複雑になってきますね。
ここらへんは様々なパターンを実装して試しながら覚えていくしかなさそうです。