12
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rustでuseを使ってパターンマッチング時にバリアントごとにEnum名を入力しなくてもいいように楽をしようとすると罠にはまる話

Posted at

こんにちは、megumishです。
今日はRustを書いてるときにはまりがちな一つの罠を紹介したいと思います。

0. 実証環境

Rust: 1.39.0 Stable (2019/11/14時点)

1. Enumの普通のパターンマッチ

まず普通にパターンマッチするとき、次のように書くかと思います。

main.rs
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を使って省略して書く時があります。

main.rs
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を使った楽をしたときの罠

さて、次に以下のようにタイポをしてしまったときのケースを考えます。

main.rs
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. なぜ意図しない結果となってしまったかの理由

さきほどの意図しない結果がでてしまったときのパターンマッチ部分をもう一度見てみましょう。

main.rs
...
    match user_kind {
        Normal => println!("普通"),
        // タイポだ!!!
        Gest => println!("ゲスト"),
        Special => println!("特別"),
    }
...

ここで前項での説明が役に立ちます。このとき、Gestという名前はどのバリアントにも当てはまらないためGestという名前に値が束縛されてしまっているのです。

試しに GestHoge などの適当な値に置き換えてみても同じ結果になるかと思います。

6. 結論:罠を踏まないために

use による楽をしないというのがまず一つの解決策なのですが、
それ以外にも使っていない束縛があるよという警告をRustのコンパイラーが出してくれるはずなのでそちらも参照すると良いかもしれません。

以上がRustのちょっとした罠の話でした。ありがとうございました。

12
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?