?の他の活用法 - Rust By Example
Description: Rust by Example (RBE) is a collection of runnable examples that illustrate various Rust concepts and standard libraries.

## Keyboard shortcuts

Press `←` or `→` to navigate between chapters

Press `S` or `/` to search in the book

Press `?` to show this help

Press `Esc` to hide this help

# `?`の他の活用法

以前の例では`parse`の呼び出しに対するその場での対応として、エラーをライブラリのエラーからboxされたエラーへと`map`していました。

    .and_then(|s| s.parse::<i32>())
        .map_err(|e| e.into())

簡単でよくあるオペレーションのため、可能なら省略してしまえると便利だったでしょう。でも残念、`and_then`が十分にフレキシブルでないため、それはできません。ただその代わり、`?`なら使えます。

`?`の挙動は、`unwrap`または`return Err(err)`として説明されていました。これはほぼ正解で、本当は`unwrap`、もしくは`return Err(From::from(err))`という意味があります。`From::from`は異なる型の間での変換ユーティリティであることから、エラーがリターン型に変換可能な場合に`?`を使うことで、その変換を自動的に行ってくれます。

前の例を`?`を使ったものに書き換えてみましょう。その結果、`From::from`がエラー型に実装されている時`map_err`は消えてなくなります。

    use std::error;
    use std::fmt;

    // エイリアスを`Box<error::Error>`に変更します。
    type Result<T> = std::result::Result<T, Box<dyn error::Error>>;

    #[derive(Debug)]
    struct EmptyVec;

    impl fmt::Display for EmptyVec {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "invalid first item to double")
        }
    }

    impl error::Error for EmptyVec {}

    // 前と同じ構造ですが、`Results`と`Option`を繋げていく代わりに、
    // `?`で内部の値をその場で取得します。
    fn double_first(vec: Vec<&str>) -> Result<i32> {
        let first = vec.first().ok_or(EmptyVec)?;
        let parsed = first.parse::<i32>()?;
        Ok(2 * parsed)
    }

    fn print(result: Result<i32>) {
        match result {
            Ok(n)  => println!("The first doubled is {}", n),
            Err(e) => println!("Error: {}", e),
        }
    }

    fn main() {
        let numbers = vec!["42", "93", "18"];
        let empty = vec![];
        let strings = vec!["tofu", "93", "18"];

        print(double_first(numbers));
        print(double_first(empty));
        print(double_first(strings));
    }

これでかなり綺麗になりました。元の`panic`と比べ、リターン型が`Result`であることを除けば、`unwrap`の呼び出しを`?`で置き換えたものに非常に似ています。結果、その`Result`は上のレベルで分解されなければなりません。

### 参照

`From::from`, `?`