web-dev-qa-db-ja.com

Iterator :: mapがResult :: Errを返すときに、反復を停止してエラーを返すにはどうすればよいですか?

Resultを返す関数があります。

fn find(id: &Id) -> Result<Item, ItemError> {
    // ...
}

次に、次のように使用します:

let parent_items: Vec<Item> = parent_ids.iter()
    .map(|id| find(id).unwrap())
    .collect();

mapのいずれかの反復内で失敗した場合の処理​​方法を教えてください。

flat_mapそしてこの場合のエラー結果は無視されます

let parent_items: Vec<Item> = parent_ids.iter()
    .flat_map(|id| find(id).into_iter())
    .collect();

Resultのイテレータには、成功状態に応じて0または1のアイテムがあり、flat_mapは、0の場合、フィルターで除外します。

しかし、私はignoreエラーをしたくありません。代わりに、コードブロック全体を停止して新しいエラーを返します(マップ内で発生したエラーに基づいて、または単に既存のエラー)。

Rustでこれを最適に処理するにはどうすればよいですか?

61
Kai Sellgren

Resultimplements FromIterator なので、Resultを外側に移動すると、イテレータが残りの処理を行います(エラーが見つかった場合の反復の停止を含む) )。

#[derive(Debug)]
struct Item;
type Id = String;

fn find(id: &Id) -> Result<Item, String> {
    Err(format!("Not found: {:?}", id))
}

fn main() {
    let s = |s: &str| s.to_string();
    let ids = vec![s("1"), s("2"), s("3")];

    let items: Result<Vec<_>, _> = ids.iter().map(find).collect();
    println!("Result: {:?}", items);
}

遊び場

75
BurntSushi5

この回答は、1.0より前のバージョンのRustに関係し、必要な関数が削除されました

std::result::fold このための関数。最初のErrが検出されると、反復が停止します。

私が書いたプログラム例:

fn main() {
  println!("{}", go([1, 2, 3]));
  println!("{}", go([1, -2, 3]));
}

fn go(v: &[int]) -> Result<Vec<int>, String> {
    std::result::fold(
        v.iter().map(|&n| is_positive(n)),
        vec![],
        |mut v, e| {
            v.Push(e);
            v
        })
}

fn is_positive(n: int) -> Result<int, String> {
    if n > 0 {
        Ok(n)
    } else {
        Err(format!("{} is not positive!", n))
    }
}

出力:

Ok([1, 2, 3])
Err(-2 is not positive!)

デモ

1
Dogbert