82979


(端的に説明できる自信が無いので、冗長ですが考えた順に記述しています…)

[Rust by Example 日本語版 > 18.5. Resultをイテレートする](https://doc.rust-jp.rs/rust-by-
example-ja/error/iter_result.html) の冒頭のコード

[code]

    fn main() {
        let strings = vec!["tofu", "93", "18"];
        let numbers: Vec<_> = strings
            .into_iter()
            .map(|s| s.parse::<i32>())
            .collect();
        println!("Results: {:?}", numbers);
        // Results: [Err(ParseIntError { kind: InvalidDigit }), Ok(93), Ok(18)]
    }
    
[/code]

に、処理を追加して数値を2倍する方法を考えています。

ぱっと思いついたのは新しい `map` を繋げてそこで2倍すれば良いだろう、というものだったので

[code]

            .map(|r| Ok(r? * 2))
    
[/code]

を追加したところ、次のコンパイルエラーになりました:

>
[code]

>     error[E0282]: type annotations needed for `Vec<Result<i32, E>>`
>  
[/code]

([playground](https://play.rust-
lang.org/?version=stable&mode=debug&edition=2018&gist=78c2386bc6f9f40658ea82668b8ac6c7))

追加した `map` について、エラーの型を明示する必要がある、と理解したので、それっぽいところに型を追加して、結果的に次のようなコードになりました:

[code]

    use std::num::ParseIntError;
    
    fn main() {
        let strings = vec!["tofu", "93", "18"];
        let numbers: Vec<_> = strings
            .into_iter()
            .map(|s| s.parse::<i32>())
            .map::<Result<_, ParseIntError>, _>(|r| Ok(r? * 2))
            .collect();
        println!("Results: {:?}", numbers);
        // Results: [Err(ParseIntError { kind: InvalidDigit }), Ok(186), Ok(36)]
    }
    
[/code]

([playground](https://play.rust-
lang.org/?version=stable&mode=debug&edition=2018&gist=d90a1dc5da0d9648e57bae4c8183c8be))

上記のコードは期待通りの挙動になっているのですが、やりたいことに関係のない、エラーについての情報(`<Result<_, ParseIntError>,
_>`)がコード内で目立ってしまっているのが気になりました。

型情報が必要なのは`?`演算子を利用しているのが理由ではないかと考え、`and_then` で書き換えてみました。これも期待通り動作しました:

[code]

            .map(|r| r.and_then(|i| Ok(i * 2)))
    
[/code]

([playground](https://play.rust-
lang.org/?version=stable&mode=debug&edition=2018&gist=9f59693332cca549191923c544465609))

ただ、この書き方だと、`map` を追加するたびに毎回その中で `and_then` を呼ぶ必要があるのですっきりしないコードになりそうです。

* * *

質問:

そもそも、`map` のクロージャが `Result` を受け取るから `Err` について考える必要が出てくるのではないかと考えました。  
`Ok` についてだけ考えれば良いような書き方はあるでしょうか。  
イメージしているのは次のようなものなのですが…:

[code]

            .map_and_then(|i: i32 /* Ok のときだけ受け取る */| Ok(i * 2))
    
[/code]

([playground](https://play.rust-
lang.org/?version=stable&mode=debug&edition=2018&gist=61ce1def82976e8269ad8f6557d36c06))

あるいはそのような書き方が無い場合、一般的にはどのように実装されるのでしょうか。


CC BY-SA 4.0
2021-10-09T15:46:28.727
82978
2021-10-09T23:36:24.470
2021-10-09T17:30:34.410
2808
2808
question
rust
map で Result を引き回すときに Err を意識したくない


いまの`numbers`を`Vec<Result<i32, _>>`型のままにしたいのでしたら、`Result::map`を使うのはどうでしょうか？

[code]

    let strings = vec!["tofu", "93", "18"];
    let numbers: Vec<_> = strings
        .into_iter()
        // Result::map()は、Ok(x)ならxにクロージャーを適用して得た値yをOkで
        // 包んでOk(y)を返す。Err(e)ならErr(e)をそのまま返す
        .map(|s| s.parse::<i32>().map(|n| n * 2))
        .collect();
    println!("Results: {:?}", numbers);
    // → Results: [Err(ParseIntError { kind: InvalidDigit }), Ok(186), Ok(36)]
    
[/code]

もし`parse`に失敗した値は無視して`numbers`を`Vec<i32>`にするなら、`Iterator::filtermap`を使うのが良さそうです。

[code]

    let strings = vec!["tofu", "93", "18"];
    let numbers: Vec<_> = strings
        .into_iter()
        // Iterator::filter_map()は、クロージャーが返した値がSomeなら
        // unwrapし、Noneならその値をスキップする
        // Result::ok()は、Ok(x)ならSome(x)を返し、Err(e)ならNoneを返す
        .filter_map(|s| s.parse::<i32>().ok())
        .map(|n| n * 2)
        .collect();
    println!("Results: {:?}", numbers);
    // → Results: [186, 36]
    
[/code]


CC BY-SA 4.0
2021-10-09T16:24:52.323
82979
2021-10-09T16:24:52.323
14101
82978
answer


[itertools](https://docs.rs/crate/itertools/) の
[`map_ok`](https://docs.rs/itertools/0.10.1/itertools/trait.Itertools.html#method.map_ok)
が利用できそうです。

[code]

    use itertools::Itertools;
    
    fn main() {
        let strings = vec!["tofu", "93", "18"];
        let numbers: Vec<_> = strings
            .into_iter()
            .map(|s| s.parse::<i32>())
            .map_ok(|i| i * 2)
            .collect();
        println!("Results: {:?}", numbers);
        // Results: [Err(ParseIntError { kind: InvalidDigit }), Ok(186), Ok(36)]
    }
    
[/code]

また、次のリンク先にスタンドアロンな実装もありました。これを理解できれば自前でも実装できるのではと考えます。

  * [What's the most idiomatic way of working with an Iterator of Results? - Stack Overflow](https://stackoverflow.com/a/36370251/4506703)


CC BY-SA 4.0
2021-10-09T23:36:24.470
82982
2021-10-09T23:36:24.470
2808
82978
answer