Rust をはじめるメンバーに伝えたい「 if より match 」
Rust をはじめるメンバーに伝えたい「 if より match 」
ぼくの所属するドクターメイト株式会社では 2023-07 から Rust を使用しています。 Rust の使用を決定した当時から現在に至るまで「メンバー全員が Rust についての知識・経験が豊富だった」なんてことはありません。常に不慣れなメンバーが居て、慣れたメンバーもより良いコードを書くために試行錯誤を繰り返しています。
「 if より match 」は、そんな新しく Rust をはじめるメンバーに伝えたいことのひとつです。
if を選択しがち
チームにジョインするメンバーは大抵の場合いくつかのプログラミング言語を経験しており、基本的なコーディングはできます。ただ、代数的データ型やパターンマッチに触れる機会に乏しかった場合、条件分岐のために if
を第一に選択する傾向があります。
// if で条件分岐をする例
let x = 0;
if x == 0 {
println!("zero but true");
} else {
println!("non-zero but false");
}
このコードは期待通りに動作します。
しかし、あえて「 if より match 」の言葉に従い、 match
を優先して検討すると良いです。
// match で条件分岐をする例
let x = 0;
match x == 0 {
true => println!("zero but true"),
false => println!("non-zero but false"),
}
この例は過剰で、 if で十分です。
ただ、意識的に match
を検討する習慣から得られるものもあります。
match で条件の考慮漏れに気づける
たとえば、条件の考慮漏れに気づけることがあります。
// 複数の条件をひとつの match で表現する例
for i in 1..=100 {
match (i % 3 == 0, i % 5 == 0) {
(false, false) => println!("{i}"),
(false, true) => println!("Buzz"),
(true, false) => println!("Fizz"),
(true, true) => println!("Fizz Buzz"),
}
}
これを if ... else if ... else ...
と記述していたら、考慮漏れしている分岐には気づきにくく (true, true)
のケースが落とし穴になることもあるでしょう。 (まさに Fizz Buzz はこの落とし穴の考慮を問うための問題ですね)
match で代数的データ型の活用につながる
ほかにも、 bool
が enum Bool { False, True }
とほとんど同じであることに気づければ、代数的データ型の活用への道が開けます。
// bool 的なデータ型の例
enum Bool {
False,
True,
}
let is_x = Bool::False;
match is_x {
Bool::False => println!("False is falsy?"),
Bool::True => println!("True is truthy?"),
}
また過剰な例で、これなら bool
で十分です。
でも、大切なのは match
なら bool
にしばられる必要はないということです。
// bool に縛られない switch のような分岐の例
enum Fruit {
Apple,
Banana,
Cucumber,
}
let fruit = Fruit::Apple;
match fruit {
Fruit::Apple => println!("Apple is fruit"),
Fruit::Banana => println!("Banana is fruit"),
Fruit::Cucumber => println!("Cucumber is fruit?"),
}
この例だと「他のプログラミング言語の switch
みたいなものかー」という理解になってしまいますが、それぞれに異なる値を持てることに気づけば代数的データ型の表現力に触れることができます。
// variant ごとに異なる値を持つ例
enum Shape {
Circle { r: f64 },
Rectangle { h: f64, w: f64 }
}
let shape = Shape::Circle { r: 2.0 };
let area = match shape {
Shape::Circle { r } => r * r * std::f64::consts::PI,
Shape::Rectangle { h, w } => h * w
};
println!("{area}");
Option
や Result
もこの流れの延長で出てきますし、データ構造で制約を表現する考えにも近づいていきます。
まとめ
「 if よりも match 」を考えることで、条件分岐での考慮漏れを減らしたり、代数的データ型とパターンマッチの組み合わせに慣れることができます。
if matches!(...)
や if let ...
のようなものはあるのですが、他のプログラミング言語から Rust に入ったときの最初の考え方として「 if より match 」を伝えるのは良いことだとぼくは考えています。
Discussion