2016-04-02 11 views
3

結果のイテレータを使用して最も慣用的な方法は何ですか?私はこのようなコードを持っています:錆:結果のイテレータをフィルタリングしてマッピングする

let things = vec![...]; // e.g. Vec<String> 
things.map(|thing| { 
    let a = try!(do_stuff(thing)); 
    Ok(other_stuff(a)) 
}).filter(|thing_result| { 
    match *thing_result { 
     Err(e) => true, 
     Ok(a) => check(a), 
    } 
}).map(|thing_result| { 
    let a = try!(thing_result); 
    // do stuff 
    b 
}).collect::<Result<Vec<_>, _>>() 

意味に関しては、最初のエラーの後で処理を停止したいと思います。

上記のコードは機能しますが、かなり面倒です。より良い方法がありますか?私はfilter_if_okのようなもののためのドキュメントを見てきましたが、何も見つかりませんでした。


編集:申し訳ありませんが、私は私の質問にもっと明示していたはずです。私はcollect::<Result<Vec<_>, _>>を知っていて、それは素晴らしい作品です。私はthing_resultmatchを使用する必要があり、フィルタの閉鎖で

  • :私は、具体的には、以下の定型を排除しようとしています。これはちょうど1ライナーにする必要があります。 .filter_if_ok(|thing| check(a))
  • 私がmapを使用するたびに、の可能性に対処するために、追加の文let a = try!(thing_result);を含める必要があります。再び、私はこれが.map_if_ok(|thing| ...)に抽象化されるように感じる。

私は簡潔に、このレベルを取得するために使用できる別のアプローチがあり、または私はちょうどそれをタフなする必要がありますか?

+0

http://stackoverflow.com/questions/26368288/dealing-with-result-within-iter? – ArtemGr

答えて

4

これらのイテレータは、自分で実装することができます。どのようにfiltermapがstdlibで実装されているかを参照してください。

map_ok実装:

#[derive(Clone)] 
pub struct MapOkIterator<I,F>{ 
    iter:I, 
    f:F 
} 

impl<A,B,E,I,F> Iterator for MapOkIterator<I,F> 
where F:FnMut(A) -> B, I:Iterator<Item=Result<A,E>> { 
    type Item = Result<B,E>; 

    #[inline] 
    fn next(&mut self) -> Option<Self::Item> { 
     self.iter.next().map(|x| x.map(&mut self.f)) 
    } 
} 

pub trait MapOkTrait{ 
    fn map_ok<F,A,B,E>(self, func:F) -> MapOkIterator<Self, F> 
    where Self:Sized+Iterator<Item=Result<A,E>>, F:FnMut(A) -> B{ 
     MapOkIterator{iter:self, f:func} 
    } 
} 

impl<I,T,E> MapOkTrait for I 
where I:Sized+Iterator<Item=Result<T,E>>{} 

filter_okほぼ同じである:

#[derive(Clone)] 
pub struct FilterOkIterator<I,P>{ 
    iter: I, 
    predicate: P, 
} 

impl<I,P,A,E> Iterator for FilterOkIterator<I,P> 
where P:FnMut(&A) -> bool, I:Iterator<Item=Result<A,E>> { 
    type Item = Result<A,E>; 

    #[inline] 
    fn next(&mut self) -> Option<Result<A,E>> { 
     for x in self.iter.by_ref() { 
      match x { 
       Ok(xx) => if (self.predicate)(&xx) { 
        return Some(Ok(xx)); 
       }, 
       Err(_) => return Some(x) 
      } 
     } 
     None 
    } 
} 

pub trait FilterOkTrait{ 
    fn filter_ok<P, A, E>(self, predicate: P) -> FilterOkIterator<Self, P> 
    where Self: Sized + Iterator<Item=Result<A,E>>, P: FnMut(&A) -> bool { 
     FilterOkIterator{iter: self, predicate: predicate} 
    } 
} 

impl<I,T,E> FilterOkTrait for I 
where I:Sized+Iterator<Item=Result<T,E>>{} 

だからあなたのコードは次のようになります。

["1","2","3","4"].iter() 
    .map(|x| x.parse::<u16>().map(|a| a+10)) 
    .filter_ok(|x| x%2==0) 
    .map_ok(|x| x+100) 
    .collect::<Result<Vec<_>, std::num::ParseIntError>>() 

playpen


廃止:

使用take_whileイテレータ:

[Ok(1),Ok(2),Err(3),Ok(4)] 
    .iter().cloned() 
    .take_while(|x| x.is_ok()) 
    .collect::<Vec<Result<u32,u32>>>() 

//[Ok(1), Ok(2)] 

また、あなたがResultのために実装FromIteratorトレイト使用することができます:あなたはできる多くの方法があります

[Ok(1),Ok(2),Err(3),Ok(4)] 
    .iter().cloned() 
    .collect::<Result<Vec<u32>,u32>>() 

//Err(3) 

[Ok(1),Ok(2),Ok(3),Ok(4)] 
    .iter().cloned() 
    .collect::<Result<Vec<u32>,u32>>() 

//Ok([1, 2, 3, 4]) 
+0

'.cloned'は必要ですか?ここは気が散っているようだ。 –

+0

申し訳ありませんが、私は 'collect :: 、_ >>'を知っていることを私の質問で明確にすべきでした。編集を参照してください。 –

+0

@Matthieu M.これは奇妙ですが、 'into_iter()'はここに値を代入しています。 – aSpex

2

filter_mapは、マッピングとフィルタリングの単純なケースを減らすために使用できます。あなたの例では、フィルタにいくつかのロジックがあるので、私はそれを単純化するとは思わない。残念なことに、Resultのドキュメントに便利な機能はありません。私はあなたの例では、それは得ることができるよう慣用的だと思いますが、ここではいくつかの小さな改善されている:

let things = vec![...]; // e.g. Vec<String> 
things.iter().map(|thing| { 
    // The ? operator can be used in place of try! in the nightly version of Rust 
    let a = do_stuff(thing)?; 
    Ok(other_stuff(a)) 
// The closure braces can be removed if the code is a single expression 
}).filter(|thing_result| match *thing_result { 
     Err(e) => true, 
     Ok(a) => check(a), 
    } 
).map(|thing_result| { 
    let a = thing_result?; 
    // do stuff 
    b 
}) 

は、あなたがそれを使用したくない場合がありますので?オペレータは、いくつかのケースでは読みにくくすることができます。

編集:あなたの代わりに偽の代わりに、真のSome(x)、およびNoneを返すためにcheck機能を変更することができるならば、あなたは使用することができfilter_map

let bar = things.iter().filter_map(|thing| { 
    match do_stuff(thing) { 
     Err(e) => Some(Err(e)), 
     Ok(a) => { 
      let x = other_stuff(a); 
      if check_2(x) { 
       Some(Ok(x)) 
      } else { 
       None 
      } 
     } 
    } 
}).map(|thing_result| { 
    let a = try!(thing_result); 
    // do stuff 
    b 
}).collect::<Result<Vec<_>, _>>(); 

あなたが使用してlet a = try!(thing);を取り除くことができますいくつかの場合においても一致する。ただし、ここでfilter_mapを使用すると役立たないようです。

8

をこれを意味する。

パニックになりたい場合は、.map(|x| x.unwrap())を使用してください。

したい場合は、すべてのは、または単一のエラーを結果Result<X<T>>collect

let results: Result<Vec<i32>, _> = result_i32_iter.collect(); 

にエラー以外のすべてをしたい場合は、.filter_map(|x| x.ok())または.flat_map(|x| x)を使用しています。

すべてまではまで、最初のエラーは.scan((), |_, x| x.ok())を使用してください。

let results: Vec<i32> = result_i32_iter.scan((), |_, x| x.ok()); 

これらの操作は、多くの場合、以前の操作と組み合わせることができます。

+0

申し訳ありません。私が '.collect :: 、_ >>'を認識していて、そのセマンティクスが自分の状況に合っていることが私の質問ではっきりしているはずです。編集を参照してください。 –

+0

'filter_map'メソッドを推奨しました。それを知らなかった、それはすごい! –

関連する問題