Rust のエラーハンドリングはシンタックスシュガーが豊富で完全に初見殺しなので自信を持って使えるように整理してみたら完全に理解した


# はじめに

* Rust の Result や Option を使ったエラーハンドリングは機能が豊富なので便利な半面、初心者にとっては難しく感じられてしまいます。
* とはいえよく見るとシンタックスシュガーが豊富なだけですごく難しいということはなかったので、自分の理解のためにも整理してまとめてみました。



# Result とは

* Result は失敗する可能性のある関数の返り値のために用意された列挙型で、関数が成功した場合の返り値のバリアントと失敗した場合の返り値のバリアントを提供します。

```rs
enum Result<T, E> {
    Ok(T), // 成功
    Err(E), // 失敗
}
```

* Result は列挙型なので `match` で評価して利用します。
* `if let` を使うと成功した場合の処理だけを記述することができます。

```rs
fn main() {
    let results = [Ok(100), Err("oops!")];

    for r in results.iter() {
        println!("Result: {:?}", r);

        let double = match r {
            Ok(v) => v * 2,
            Err(_) => 0,
        };
        println!("match result: {}", double);

        if let Ok(v) = r {
            println!("if let result: {}", v);
        }

        println!("")
    }
}
```

```:標準出力
Result: Ok(100)
match result: 200
if let result: 100

Result: Err("oops!")
match result: 0
```

# Option とは

Option は空になる可能性のある値を表現するために用意された列挙型で、空の場合のバリアントと値のある場合のバリアントを提供します。

```rs
enum Option<T> {
    None, // 値が空
    Some(T), // 値がある
}
```

* Option も列挙型なので `match` や `if let` で評価して利用します。

```rs
fn main() {
    let options = [Some(-1), None];

    for o in options.iter() {
        println!("Option: {:?}", o);

        let double = match o {
            Some(v) => v * 2,
            None => 0
        };
        println!("match result: {}", double); //=> -2

        if let Some(v) = o {
            println!("if let result: {}", v);
        }

        println!("")
    }
}
```

```:標準出力
Option: Some(-1)
match result: -2
if let result: -1

Option: None
match result: 0
```

# unwrap メソッド

* Rust のエラーハンドリングで一番目につまづくのが unwrap メソッドかなと思います。
  * 便利なのでサンプルコードなんかでよく使われていますが、ちゃんと Result の使い方を知る前に出てきちゃったりするので初心者的にはおまじないに見てしまっていてあまり良くないです。

## 解説

* Result.unwrap メソッドは Ok なら中の値を返し、Err なら panic を起こします。

```rs
fn main() {
    let result: Result<i32, String> = Ok(2);
    println!("{:?}", result.unwrap()); //=> 2
    
    let result: Result<i32, String> = Err("error".to_string());
    println!("{:?}", result.unwrap()); //=> panic!
}
```

* Option も unwrap メソッドを持っており、Result と同じように Some なら中の値を返し、None なら panic を起こします

```rs
fn main() {
    let option: Option<i32> = Some(2);
    println!("{:?}", option.unwrap()); //=> 2
    
    let option: Option<i32> = None;
    println!("{:?}", option.unwrap()); //=> panic
}
```

* このように unwrap を使うと match や if let などを使わすに一行で手軽に Ok の中の値を取得できるので便利です
* が、失敗すると panic を起こすので、実際に利用する際にはチェックしてから使用するか後述する別のメソッドを使うかと思います。

## panic を起こさない unwrap の類似メソッド

* unwrap の類似メソッドで panic の代わりに任意のデフォルト値を指定することができる `unwrap_or` と panic の代わりにクロージャを実行した値を返す `unwrap_or_else` があります。

```rs
fn main() {
    let result: Result<i32, String> = Err("error".to_string());
    println!("{:?}", result.unwrap_or(10)); //=> 10

    let result: Result<i32, String> = Err("error".to_string());
    println!("{:?}", result.unwrap_or_else(|| 20)); //=> 20

    let option: Option<i32> = None;
    println!("{:?}", option.unwrap_or(100)); //=> 100
    
    let option: Option<i32> = None;
    println!("{:?}", option.unwrap_or_else(|| 200)); //=> 200
}
```


# ? 演算子

* Rust のエラーハンドリングで二番目につまづくのが ? 演算子だと思います。
  * これもサンプルコードに気軽に出てきますが説明が足りないのでおまじないになってしまっていますし、類似の概念があまり他の言語にないのでなかなか手強いです。

## 解説

* ? 演算子は Result を返す関数の中で利用されます。
* ? 演算子は Result 型の値の後ろにつけて利用され、値が Ok なら中の値を返し、値が Err なら即座に値を return します。
* わかりやすくするため `let left = right?;` を擬似コードにするとこのようになります。right は Result<i32> 型の変数です。

```rs
let left: i32;
// is_ok() は right が Ok なら ture を返します
if right.is_ok() {
    left = right.unwrap();
} else {
    return right;
}
```

* ? 演算子が便利なのは Result を受け取ったときにエラーならすぐに処理を中止して return したいというときで、よくあるこんな処理を少ない記述量で手軽に実装することができます。

## 例

* 実際に ? 演算子を使っているコードをみてみましょう。
* Result を返す `double` 関数のなかで同じく Result を返す `odd` が呼び出され ? 演算子を使ってエラーがハンドリングされています。

```rs
fn main() {
    let odd = |n| -> Result<i32, String> {
        if n % 2 == 1 {
            Ok(n)
        } else {
            Err(format!("{} is not odd", n))
        }
    };

    let double = |n| -> Result<i32, String> {
        let n = odd(n)?; // odd が Err ならすぐに return する
        return Ok(n * 2);
    };

    for n in 0 .. 4 {
        println!("number: {}", n);
        println!("double result: {:?}\n", double(n));
    }
}
```

```:標準出力
number: 0
double result: Err("0 is not odd")

number: 1
double result: Ok(2)

number: 2
double result: Err("2 is not odd")

number: 3
double result: Ok(6)
```

* Rust と同じようにエラーを返り値でハンドリングする Go を利用したことがあるとわかると思いますが `if err != nil { return err }` のようなコードを延々と書く必要がなくなるというのが ? 演算子のいいところですね。（Go のほうでもシンタックスシュガーを用意しようとしているらしいですが）

## Option でも

* ? 演算子は Option でも同様に利用することができて、Some なら中の値を返し None ならすぐに return します。

```rs
fn main() {
    let double = |n: Option<i32>| -> Option<i32> {
        println!("Option: {:?}", n);
        let m = n?;
        return Some(m * 2);
    };
    println!("{:?}\n", double(Some(100)));
    println!("{:?}", double(None));
}
```

```:標準出力
Option: Some(100)
Some(200)

Option: None
None
```

# Result のいろいろな操作

## 成功した時だけ中の値を加工する `map`

* `map` はクロージャを受け取り Result の値が `Ok` の時だけクロージャを実行して中の値を加工します。値が `Err` なら何もしません。

```rs
fn main() {
    let results = [Ok(100), Err("Error")];
    
    println!("{:?}", results[0].map(|n| n * 2)); //=> Ok(200)
    println!("{:?}", results[1].map(|n| n * 2)); //=> Err("Error")
}
```

* 失敗した時だけ中の値を加工したい場合は `map_err` メソッドが用意されています。

## 成功した時だけ処理を実行する `and_then`

* `and_then` は `map` と同様に Result の値が Ok の時だけクロージャを実行します。
* `map` が Ok の中の値を加工するのに対して、`and_then` はその名の通りに任意の処理を実行して新たな Result を作成して返却します。
* `and_then` の入力値と出力値で Result の型が変わる様な使い方もできます。

```rs
fn main() {
    let results = [Ok(100), Err("Error")];
    
    let f = |n| Ok(n * 2);
    println!("{:?}", results[0].and_then(f)); //=> Ok(200)
    println!("{:?}", results[1].and_then(f)); //=> Err("Error")
    
    let f = |n| Ok(format!("number is {}", n));
    println!("{:?}", results[0].and_then(f)); //=> Ok("number is 100")
}
```

* 失敗した時だけ処理を実行したい場合は `or_else` メソッドが用意されています。

## Result から Option に変換する `ok` `err`

* `ok` は `Result<T, E>` を `Option<T>` に変換します
* `err` は `Result<T, E>` を `Option<E>` に変換します

```rs
fn main() {
    let results = [Ok(100), Err("err")];

    println!("{:?}", results[0].ok()); //=> Some(100)
    println!("{:?}", results[0].err()); //=> None
    println!("{:?}", results[1].ok()); //=> None
    println!("{:?}", results[1].err()); //=> Some("err")
}
```

## 中の値を参照にする `as_ref` `as_mut`

* `as_ref` は `&Result<T, E>` を `Result<&T, &E>` に変換します
* `as_mut` は `&mut Result<T, E>` を `Result<&mut T, &mut E>` に変換します


## Result 同士の和や積を求める `and` `or`

* `and` メソッドや `or` メソッドを使うと boolean のように Result 同士の和や積を求めることができる

```rs
fn main() {
    let results = [Ok(1), Ok(2), Err("E1"), Err("E2")];
    
    println!("{:?}", results[0].and(results[1])); //=> Ok && Ok   = Ok(2)
    println!("{:?}", results[0].and(results[2])); //=> Ok && Err  = Err("E1")
    println!("{:?}", results[2].and(results[1])); //=> Err && Ok  = Err("E1")
    println!("{:?}", results[2].and(results[3])); //=> Err && Err = Err("E1")
    
    println!("{:?}", results[0].or(results[1])); //=> Ok || Ok   = Ok(1)
    println!("{:?}", results[0].or(results[2])); //=> Ok || Err  = Ok(1)
    println!("{:?}", results[2].or(results[1])); //=> Err || Ok  = Ok(2)
    println!("{:?}", results[2].or(results[3])); //=> Err || Err = Err("E2")
}
```

## その他のメソッド

* 紹介できていないものもあるので知らないメソッドが出てきたら[公式ドキュメント](https://doc.rust-lang.org/std/result/enum.Result.html)を読むのが良いと思います。

# Option のいろいろな操作

## 値がある時だけ中の値を加工する `map`

* `Option.map` は値があればクロージャを実行して中の値を加工し、空なら何もしません。

```rs
fn main() {
    let options = [Some(100), None];

    println!("{:?}", options[0].map(|n| n * 2)); //=> Some(200)
    println!("{:?}", options[1].map(|n| n * 2)); //=> None
}
```

## 値がある時だけ処理を実行する `and_then`

* `and_then` は `map` と同様に Option の値が Some の時だけクロージャを実行します。
* `map` が Some の中の値を加工するのに対して、`and_then` はその名の通りに任意の処理を実行して新たな Option を作成して返却します。
* `and_then` の入力値と出力値で Option の型が変わる様な使い方もできます。

```rs
fn main() {
    let options = [Some(100), None];

    let f = |n| Some(n * 2);
    println!("{:?}", options[0].and_then(f)); //=> Some(200)
    println!("{:?}", options[1].and_then(f)); //=> Err("Error")

    let f = |n| Some(format!("number is {}", n));
    println!("{:?}", options[0].and_then(f)); //=> Some("number is 100")
}
```

* 値が空の時だけ処理を実行したい場合は `or_else` メソッドが用意されています。

## Option から Result に変換する `ok_or`

* `ok_or<E>` は `Option<T>` を `Result<T, E>` に変換します

```rs
fn main() {
    let options = [Some(100), None];

    println!("{:?}", options[0].ok_or("err")); //=> Ok(100)
    println!("{:?}", options[1].ok_or("err")); //=> Err("err")
}
```

## Option 同士の和や積を求める `and` `or`

* `and` メソッドや `or` メソッドを使うと boolean のように Option 同士の和や積を求めることができる

```rs
fn main() {
    let options = [Some(100), Some(200), None, None];

    println!("{:?}", options[0].and(options[1])); //=> Some && Some = Some(200)
    println!("{:?}", options[0].and(options[2])); //=> Some && None = None
    println!("{:?}", options[2].and(options[1])); //=> None && Some = None
    println!("{:?}", options[2].and(options[3])); //=> None && None = None

    println!("{:?}", options[0].or(options[1])); //=> Some || Some = Some(100)
    println!("{:?}", options[0].or(options[2])); //=> Some || None = Some(100)
    println!("{:?}", options[2].or(options[1])); //=> None || Some = Some(200)
    println!("{:?}", options[2].or(options[3])); //=> None || None = None
}
```

## その他のメソッド

* 紹介できていないものもあるので知らないメソッドが出てきたら[公式ドキュメント](https://doc.rust-lang.org/std/option/enum.Option.html)を読むのが良いと思います。


# おわりに

* Result も Option もシンタックスシュガーが多くて初見だと不安になってしまいますが、`*_then` ならポジティブな値のときにクロージャを実行するとか `*_else` ならその逆だとか `*_or` ならデフォルト値を受け取るとか、語感的になんとなく判別できるので、半分くらいおぼえておいて困ったらドキュメントを見る感じでなんとかなる気がします。