コンテンツにスキップ

Rustのmatch

match は、ある値をパターンの一覧と順番に比べて、当てはまったパターンのコードを実行します。ほかの言語の switch 文を使ったことがあれば、それをもっと強力にしたものだと考えてください。使ったことがなくても大丈夫です。早いうちに覚えておくと、とても役に立つ書き方です。

fn main() {
let day = 3;
match day {
1 => println!("月曜日"),
2 => println!("火曜日"),
3 => println!("水曜日"),
4 => println!("木曜日"),
5 => println!("金曜日"),
_ => println!("週末"),
}
}

実行結果:

水曜日
▶ 実際に試してみよう

_ は「それ以外すべて」に当てはまるパターンです。上に書いたどのパターンにも当てはまらない値は、ここで受け止められます。match では、ありうるすべての値を処理しなければなりません。そのため、たいていは _ を置いて「それ以外」をまとめて扱います。

複数の値をまとめてマッチする

Section titled “複数の値をまとめてマッチする”

| を使うと、いくつかの値を1つのアーム(腕)にまとめられます。

fn main() {
let day = 6;
match day {
1 | 2 | 3 | 4 | 5 => println!("平日"),
6 | 7 => println!("週末"),
_ => println!("正しい曜日ではありません"),
}
}
▶ 実際に試してみよう

数値なら、..= を使って範囲をまるごとマッチできます。

fn main() {
let score = 85;
match score {
90..=100 => println!("A"),
75..=89 => println!("B"),
60..=74 => println!("C"),
_ => println!("F"),
}
}
▶ 実際に試してみよう

if と同じように、match も値を返せます。

fn main() {
let day = 6;
let kind = match day {
1 | 2 | 3 | 4 | 5 => "平日",
6 | 7 => "週末",
_ => "不明",
};
println!("{kind}");
}
▶ 実際に試してみよう

match は数値専用ではありません。文字列スライス(&str)や文字(char)にも使えます。'a'..='z' のような文字の範囲も、数値の範囲と同じように使えます。

fn main() {
let command = "stop";
match command {
"start" => println!("開始します..."),
"stop" | "quit" => println!("停止します。"),
_ => println!("不明なコマンド: {command}"),
}
let letter = 'k';
let kind = match letter {
'a' | 'e' | 'i' | 'o' | 'u' => "母音",
'a'..='z' => "子音",
'0'..='9' => "数字",
_ => "それ以外",
};
println!("'{letter}' は{kind}です");
let name = String::from("Ferris");
match name.as_str() {
"Ferris" => println!("やあ、カニくん!"),
_ => println!("こんにちは、{name}さん!"),
}
}

実行結果:

停止します。
'k' は子音です
やあ、カニくん!
▶ 実際に試してみよう

ここで注目してほしい点が2つあります。

  • 順番が大切です。'a' は 'a'..='z' にも含まれますが、母音のアームが先にあるので、母音が子音のアームまで届くことはありません。Rustは、当てはまる最初のアームを必ず選びます。
  • String には .as_str() が必要です。"Ferris" のように引用符で囲んで書いたテキストは &str です。String をそのまま match でこれと比べようとすると、Rustは error[E0308]: mismatched types ... expected String, found &str(「型が一致しません。String を期待していたのに &str が見つかりました」)というエラーで止まります。.as_str() を呼ぶと、String を &str として見られるようになり、パターンと比べられます(2つの違いは文字列のページを見てください)。

マッチは大文字と小文字を区別します。"Stop" は "stop" には当てはまりません。ユーザーが入力したテキストなら、input.to_lowercase().as_str() でマッチするのがよく使われる方法です。

単純な真偽のチェックなら、どちらでも書けます。でも、分岐が3つ、4つと増えてくると match のほうが有利です。長い else if の連なりより読みやすく、すべてのケースを書いたかどうかもコンパイラが確かめてくれます。一方、「はい/いいえ」の条件が1つだけなら、今でも if/else がぴったりです。

月の番号 month(2)を保存してください。match を式として使ってその月が何日あるかを求め(うるう年は考えなくてかまいません)、2月は28日あります と表示しましょう。ヒント:1、3、5、7、8、10、12月は31日です。| を使って1つのアームにまとめましょう。

解答を見る
fn main() {
let month = 2;
let days = match month {
1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
4 | 6 | 9 | 11 => 30,
2 => 28,
_ => 0, // 実在しない月
};
println!("{month}月は{days}日あります");
}

実行結果:

2月は28日あります
▶ 実際に試してみよう

Webサーバーは数値で返事をします。code を短い意味に変換してください。200 は "OK"、404 は "Not Found"、それ以外の400〜499は "クライアントエラー"、500〜599は "サーバーエラー"、それ以外はすべて "不明" です。503 で試してみましょう。ヒント:404 ちょうどのアームは、400..=499 の範囲の上に書いてください。そうしないと、決してそこまで届きません。

解答を見る
fn main() {
let code = 503;
let meaning = match code {
200 => "OK",
404 => "Not Found", // 400..=499 の範囲より前に書く必要がある
400..=499 => "クライアントエラー",
500..=599 => "サーバーエラー",
_ => "不明",
};
println!("{code}: {meaning}");
}

実行結果:

503: サーバーエラー
▶ 実際に試してみよう
  • match は値をパターンと比べて、当てはまったアームを実行します。
  • ありうるすべての値を処理する必要があります。「それ以外すべて」には _ を使います。
  • | で値をまとめたり、..= で範囲をマッチしたりできます。
  • &str のテキストや char の値でもマッチできます。String でマッチするには .as_str() を使います。
  • match も if と同じく、式として値を返せます。

理解度チェック

1. match式の中で、_ パターンはどんな働きをしますか?

2. match式がありうるすべての値を処理しておらず、_ のアームもない場合はどうなりますか?

3. 1、2、3のような複数の値を1つのアームでマッチするには、どう書きますか?

スコア: 0 / 3