見出し画像

RustのOption型を使う利点

Option型とは?

Option型はNoneとSome<T>の値を取りうる列挙型です。
例えばOption<i32>は32bit整数の値が入ったSome<i32>かNoneの値を取りえます。

pub enum Option<T> {
    None,
    Some(T),
}

普通のプログラミング言語にはnullがあり、ある値がnullであった時の処理を書く必要があります。
これを忘れるとnullと整数を足すなどの計算をしようとしてバグの原因になります。
下記はRubyの例です。
xやyがnilだった場合は片方のみを返しています。(nil.to_iは0になるのでx.to_i+y+to_iでも良いのですが、説明のためこうしています)

x = 1
y = nil

def sum(x, y)
    return y if x == nil
    return x if y == nil
    
    x + y
end

puts sum(x, y)


RustのOption型を使うと何が嬉しいか

Rustにはmatch式と言うものがあり、それとOption型を使うと上のコードは下記のように書けます。

fn sum(x: Option<i32>, y: Option<i32>) -> Option<i32> {
    match (x, y) {
        (None, None) => None,
        (Some(x), None) => Some(x),
        (None, Some(y)) => Some(y),
        (Some(x), Some(y)) => Some(x + y),
    }
}

fn main() {
    let x = Some(1);
    let y: Option<i32> = None;

    match sum(x, y) {
        Some(result) => println!("{}", result),
        None => println!("None"),
    }
}

どちらもNoneの時はNoneを返し、どちらかのみ値があるときは値がある値、どちらも値がある時は足した値を返しています。

もしsumの関数にあるmatch式のブロックの最後を消すとどうなるでしょうか?
もし(Some(x), Some(y)) => Some(x + y),をコメントアウトすると下記のようなエラーとなります。
これは全てのパターンが網羅されていないと言うエラーです。

error[E0004]: non-exhaustive patterns: `(Some(_), Some(_))` not covered
 --> Main.rs:4:11
  |
4 |     match (x, y) {
  |           ^^^^^^ pattern `(Some(_), Some(_))` not covered
  |
  = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
  = note: the matched value is of type `(Option<i32>, Option<i32>)`

Optionとmatch式を組み合わせることによって、他の言語では起こりがちなnullチェックを忘れてしまうことによるバグを防ぐことができます。

まとめ

今回はOption型とmatch式を組み合わせることによって、値が存在しない可能性を安全に扱うことができると言うことを学びました。
Rustは勉強途中なので間違いがあればお気軽にご指摘いただけるとありがたいです。


この記事が気に入ったらサポートをしてみませんか?