【Rust入門】HashMap 型の基本について分かりやすく解説

Rust でデータをキーと値のペアで管理するための HashMap 型の基本を初心者にも分かりやすく解説します。
HashMap<K, V> 型
Rust には、同じ型のデータをまとめて管理するための「コレクション(Collections)」が用意されています。コレクションは、実行中に要素数を変更できる柔軟なデータ構造です。
この記事では、コレクション型の代表例である HashMap<K, V> 型について解説します。
HashMap<K, V> 型とは
HashMap<K, V> 型は、キー(Key)と値(Value)のペアでデータを管理するためのコレクション型です。
Vec<T> 型がインデックスで要素にアクセスするのに対して、HashMap はキーを使って値を取得します。コレクション型は、内部でヒープ領域を利用してデータを管理するため、プログラム実行時のデータ長の変更や大きなサイズのデータの扱いに適します。
例えば、「ユーザー ID からユーザー名を取得する」「商品 ID から価格を取得する」というような場合を扱う際のデータ管理に非常に適しています。
HashMap の生成方法
HashMap を使う準備
HashMap は標準ライブラリの std::collections モジュールで提供されています。利用するには以下のようにインポートします。
use std::collections::HashMap;
以降で紹介する各プログラムでも先頭でインポートをしています。
関連関数 HashMap::new() を使用する
HashMap 型を生成する最も基本的な方法は、new メソッドを使用する方法です。キーや値を追加するには、insert メソッドを使用します。
use std::collections::HashMap;
fn main() {
// HashMap を生成する
let mut scores = HashMap::new();
// キー、値を追加する
scores.insert(String::from("Alice"), 80);
scores.insert(String::from("Bob"), 100);
println!("{:?}", scores);
}【実行結果】
{"Alice": 80, "Bob": 100}なお、HashMap は要素の順序を保証しません。そのため、実行により表示順序が異なる場合があります。
collect メソッドを使用する
HashMap は、イテレータから collect メソッドを使用して生成することも可能です。
use std::collections::HashMap;
fn main() {
let keys = vec![String::from("Alice"), String::from("Bob")];
let values = vec![80, 10];
let scores: HashMap<String, i32> = keys.into_iter().zip(values.into_iter()).collect();
println!("{:?}", scores);
}【実行結果】
{"Alice": 80, "Bob": 10}上記例では、keys と values という Vec 型の変数を用意し、zip によりペアを生成して collect により HashMap 型に集約しています。collect メソッドは任意のコレクションにまとめることができるため、型注釈をつけるようにしてください。
HashMap 要素の取得・追加・更新・削除
要素の取得
HashMap の要素値を取得するには、get メソッドによりキーを指定します。
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
// Alice のスコアを追加
scores.insert(String::from("Alice"), 80);
// Alice のスコアを取得
let alice_score = scores.get("Alice");
match alice_score {
Some(score) => println!("Alice のスコア : {}", score),
None => println!("Alice のスコアが見つかりませんでした。"),
}
// Bob のスコアを取得(存在しないキー)
let bob_score = scores.get("Bob");
match bob_score {
Some(score) => println!("Bob のスコア : {}", score),
None => println!("Bob のスコアが見つかりませんでした。"),
}
}【実行結果】 Alice のスコア : 80 Bob のスコアが見つかりませんでした。
get は、Option<&V> 型 を返却するため、match を使って値の有無を確認して安全に処理することが可能です。キーが存在しない場合には、None となります。
要素の追加・更新
新しい要素の追加や更新には、insert メソッドを使用します。例のように、同じキーに対して insert を実行すると更新となる点に注意してください。
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
println!("初期状態 : {:?}", scores);
// スコアを追加する
scores.insert(String::from("Alice"), 80);
scores.insert(String::from("Bob"), 100);
println!("追加後 : {:?}", scores);
// Alice のスコアを更新する(上書き)
scores.insert(String::from("Alice"), 95);
println!("更新後 : {:?}", scores);
}【実行結果】
初期状態 : {}
追加後 : {"Bob": 100, "Alice": 80}
更新後 : {"Bob": 100, "Alice": 95}なお、String のような所有権を持つ型の場合、変数を使用している場合は、insert 時に所有権が移動し、元の変数は使えなくなるので注意しましょう。
let name = String::from("Alice");
scores.insert(name, 80);
// 所有権が移動するためエラーとなる。
// println!("{name}");要素の削除
要素を削除する場合には、remove メソッドを使用します。存在しないキーを削除しても何も起こりません。
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Alice"), 80);
scores.insert(String::from("Bob"), 100);
scores.insert(String::from("Charlie"), 70);
println!("削除前 : {:?}", scores);
// キーを指定して削除する
scores.remove("Alice");
println!("Alice 削除後 : {:?}", scores);
// 存在しないキーを削除しても何も起きない
scores.remove("Dave");
println!("Dave 削除後(変化なし) : {:?}", scores);
}【実行結果】
削除前 : {"Bob": 100, "Charlie": 70, "Alice": 80}
Alice 削除後 : {"Bob": 100, "Charlie": 70}
Dave 削除後(変化なし) : {"Bob": 100, "Charlie": 70}存在しない場合のみ値を追加する場合(Entry API)
HashMap にキーが存在しない場合にのみ値を追加したい場合は、Entry API を使用するのが便利です。entry により対象を取得し、or_insert で値を追加することで、存在しない場合にのみ値を追加することができます。
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Alice"), 80);
println!("初期状態 : {:?}", scores);
// Alice はすでに存在するので更新されない
scores.entry(String::from("Alice")).or_insert(0);
// Bob は存在しないので追加される
scores.entry(String::from("Bob")).or_insert(100);
println!("or_insert 後 : {:?}", scores);
}【実行結果】
初期状態 : {"Alice": 80}
or_insert 後 : {"Bob": 100, "Alice": 80}繰り返し処理(for)での利用方法
HashMap は複数の要素を管理するため、for を利用した繰り返し処理を行うことがよくあります。Rust の繰り返しでは、イテレータを取得して動作します。
参照のみの繰り返し処理
HashMap の要素を 1 つずつ取り出して参照するだけの繰り返しの場合には、以下のように不変参照 (&) を使って for を使用します。
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Alice"), 80);
scores.insert(String::from("Bob"), 100);
scores.insert(String::from("Charlie"), 70);
// 所有権を移動せず参照としてループする
for (name, score) in &scores {
println!("{name} のスコア : {score}");
}
// ループ後も scores を使用できる
println!("ループ後のエントリ数 : {}", scores.len());
}【実行結果】 Bob のスコア : 100 Alice のスコア : 80 Charlie のスコア : 70 ループ後のエントリ数 : 3
キーと値は、(name, score) にそれぞれ 1 要素ずつ取り出して繰り返し処理が進みます。
値の変更を伴う繰り返し処理
HashMap の要素を繰り返しの中で変更する場合には、以下のように可変参照 (&mut) を使って for を使用します。
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Alice"), 80);
scores.insert(String::from("Bob"), 100);
scores.insert(String::from("Charlie"), 70);
println!("更新前 : {:?}", scores);
// 可変参照でループし、全員のスコアに 10 を加算する
for (_name, score) in &mut scores {
*score += 10;
}
println!("更新後 : {:?}", scores);
}【実行結果】
更新前 : {"Alice": 80, "Bob": 100, "Charlie": 70}
更新後 : {"Alice": 90, "Bob": 110, "Charlie": 80}値を変更する場合には、*score のように参照を外して変更することが可能です。
所有権の移動を伴う繰り返し処理
HashMap の所有権を移動して繰り返し処理をする場合には、以下のように in の部分に HashMap の変数を直接指定します。
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Alice"), 80);
scores.insert(String::from("Bob"), 100);
scores.insert(String::from("Charlie"), 70);
// 所有権を移動させてループする
for (name, score) in scores {
println!("{name} のスコア : {score}");
}
// 所有権が移動したため、ループ後は scores を使用できない
// println!("{:?}", scores); // コンパイルエラー
}【実行結果】 Charlie のスコア : 70 Bob のスコア : 100 Alice のスコア : 80
所有権を移動する繰り返し処理をした場合には、繰り返し後には変数を使うことができなくなります。この方法は、ループ後に元の HashMap を使わない場合や大きいサイズの HashMap のためループ終了にあわせてメモリ解放したい場合などに利用できます。
まとめ
Rust でデータをキーと値のペアで管理するための HashMap 型の基本を解説しました。
HashMap<K, V> 型は、キーと値のペアでデータを管理するためのコレクション型です。ユーザー ID とユーザー名、商品 ID と価格など、対応関係を持つデータを効率的に管理できます。
この記事では、HashMap の生成方法から要素の取得・追加・更新・削除、Entry API を利用した値の追加方法、for を使った繰り返し処理まで紹介しました。
HashMap は Rust のプログラムで非常によく利用されるコレクション型です。Vec 型とあわせて使う機会も多いため、この記事で紹介した基本操作をしっかり身につけておきましょう。
上記で紹介しているソースコードについては GitHub にて公開しています。参考にしていただければと思います。







