こんにちは、megumishです。
今日はRustを書いてるときにはまりがちな一つの罠を紹介したいと思います。
0. 実証環境
Rust: 1.39.0 Stable (2019/11/14時点)
1. Enumの普通のパターンマッチ
まず普通にパターンマッチするとき、次のように書くかと思います。
fn main() {
message(UserKind::Normal);
message(UserKind::Guest);
message(UserKind::Special)
}
fn message(user_kind: UserKind) {
match user_kind {
UserKind::Normal => println!("普通"),
UserKind::Guest => println!("ゲスト"),
UserKind::Special => println!("特別"),
}
}
enum UserKind {
Normal,
Guest,
Special,
}
2. useを使った楽をしたパターンマッチングの書き方
しかし、さきほどのように書くとパターンマッチングの際にいちいちバリアントごとに UserKind::
とつけなくてはなりません。
このようにしたくないとき、以下のようにuseを使って省略して書く時があります。
fn main() {
message(UserKind::Normal);
message(UserKind::Guest);
message(UserKind::Special)
}
fn message(user_kind: UserKind) {
use UserKind::*;
match user_kind {
Normal => println!("普通"),
Guest => println!("ゲスト"),
Special => println!("特別"),
}
}
enum UserKind {
Normal,
Guest,
Special,
}
このように書くと、 UserKind::
といちいち書く必要がなくなり楽です。よさげですね!!!
3. useを使った楽をしたときの罠
さて、次に以下のようにタイポをしてしまったときのケースを考えます。
fn main() {
message(UserKind::Normal);
message(UserKind::Guest);
message(UserKind::Special)
}
fn message(user_kind: UserKind) {
use UserKind::*;
match user_kind {
Normal => println!("普通"),
// タイポだ!!!
Gest => println!("ゲスト"),
Special => println!("特別"),
}
}
enum UserKind {
Normal,
Guest,
Special,
}
このとき、バリアント名が間違っているためコンパイルは通ってほしくないと考えると思います。
けれども、このコードのコンパイルは通り無慈悲にも以下のような想定しないであろう出力をしてしまいます。
普通
ゲスト
ゲスト
4. パターンマッチと任意の値の束縛
通常、パターンマッチ時に任意の値を入れたいときに次のような記述をするかと思います。
match user_kind {
Normal => println!("普通"),
x => println!("普通ではない"),
}
なぜ突然こんな話をしたいかというとこれにさっきの罠が起こったヒントがあるからです。
5. なぜ意図しない結果となってしまったかの理由
さきほどの意図しない結果がでてしまったときのパターンマッチ部分をもう一度見てみましょう。
...
match user_kind {
Normal => println!("普通"),
// タイポだ!!!
Gest => println!("ゲスト"),
Special => println!("特別"),
}
...
ここで前項での説明が役に立ちます。このとき、Gest
という名前はどのバリアントにも当てはまらないためGest
という名前に値が束縛されてしまっているのです。
試しに Gest
を Hoge
などの適当な値に置き換えてみても同じ結果になるかと思います。
6. 結論:罠を踏まないために
use
による楽をしないというのがまず一つの解決策なのですが、
それ以外にも使っていない束縛があるよという警告をRustのコンパイラーが出してくれるはずなのでそちらも参照すると良いかもしれません。
以上がRustのちょっとした罠の話でした。ありがとうございました。