2016-09-27 6 views
1

コロンで区切られた値を持つStringがあります。各サブストリングはマップ内のキーでなければなりません。 Stringをマップ値のVecに変換する関数を記述したいと思います。キーがない場合は失敗します。ハッシュマップルックアップを使用したイテレータマップ。いずれのキーでも失敗する

これまでのところ、私の試みは不完全ですが、私はそれを少しずつ取り上げています。最初のステップは、文字列をオプションのu32のベクトルに変換することです。 (これが最善の方法ではない場合があります):これは

error: the trait bound `std::vec::Vec<std::option::Option<u32>>: std::iter::FromIterator<std::option::Option<&u32>>` is not satisfied [--explain E0277] 
--> <anon>:8:52 
    |> 
8 |>  string.split(":").map(|s: &str| lookup.get(s)).collect() 
    |>             ^^^^^^^ 
note: a collection of type `std::vec::Vec<std::option::Option<u32>>` cannot be built from an iterator over elements of type `std::option::Option<&u32>` 

になり

fn parse(string: &String, lookup: HashMap<String, u32, FastHasher>) -> Vec<Option<u32>> { 
    string.split(":").map(|s: &str| lookup.get(s)).collect() 
} 

私は、これは私が右、Option<&u32>のための私自身から、イテレータ行動をインポートまたは書き込みする必要があることを意味信じますか?

これを実行した後、Noneの存在に応じて、OkまたはErrをどのようにラップできますか?この問題を回避するためには

+3

返品の種類を忘れましたか? – starblue

+0

私はそれがそのままの文章でどのような問題があるのか​​見逃しました。私はこのコードのいくつかの長い遠隔反復で戻り値の型を持っていました。更新しました。 – Synesso

答えて

1

あなたが本当に参照を返すようにしたい場合は、明示的に寿命を指定する必要があります。

以降:

pub fn parse<'a, 'b>(string: &'a str, lookup: &'b HashMap<String, u32>) -> Vec<Option<&'b u32>> { 
    string.split(":").map(|s| lookup.get(s)).collect() 
} 

Result型への変換についての質問の後半部分について私はそれをしましたまたはErrに に応じてどのようにラップできますか?Noneの存在がありますか?

これは、結果をOk<Vec<u32>>にフォールディングして累積することによって実行できます。次の例はそのアイディアを示しています。実行可能な例はRust Playgroundを参照してください。

use std::collections::HashMap; 
use std::result::Result; 

#[derive(Debug)] 
pub struct ParseError { 
    key: String, 
} 

impl ParseError { 
    fn new(k: &str) -> ParseError { 
     ParseError { key: k.to_owned() } 
    } 
} 

fn parse(string: &str, lookup: &HashMap<String, u32>) -> Result<Vec<u32>, ParseError> { 
    string.split(":") 
     .fold(Ok(Vec::new()), |res, s| { 
      let mut vec = try!(res); 
      match lookup.get(s) { 
       Some(&v) => { 
        vec.push(v); 
        Ok(vec) 
       } 
       None => Err(ParseError::new(s)), 
      } 
     }) 
} 
3

、代わりにVec<Option<&u32>>のあなただけのVec<Option<u32>>を返すことができます:

はマチューが提案されているように簡略化することができ、
fn parse(string: &String, lookup: HashMap<String, u32>) -> Vec<Option<u32>> { 
    string.split(":").map(|s: &str| if let Some(&e) = lookup.get(s) { Some(e) } else { None }).collect() 
} 

string.split(":").map(|s: &str| lookup.get(s).cloned()).collect() 

が、私はわからないんだけどResultに値をラップすると値が加算されます。あなたは簡単に後でNoneを確認することができます。

let v = vec![Some(1), None, Some(3)]; 
println!("{:?}", v.contains(&None)); 
+1

注: 'Option <&T>'から 'Option 'に移動するには、 'Option :: cloned'を使用してください。これはより短いコードのためになります:' string.split( ":"))map(| s:&str | lookup.get(s).cloned())。collect() ' –

+0

これについては分かりませんでした。私はそれを答えに含めるでしょう。 – ljedrz

6

HashMap::get()方法は、マップ内の値にオプション参照を返します。したがって、イテレータはOption<&u32>以上ですが、イテレータはOption<u32>以上にする必要があります。これは、次のようにして行われます。

lookup.get(s).cloned() 
//   ^^^^^^^^^ 

これにより、現在のコードがコンパイルされます。


あなたのタイトルで質問に答えるために:小ぎれいなFromIterator IMPLがある:

impl<A, V> FromIterator<Option<A>> for Option<V> where V: FromIterator<A> 

このことは、たとえば、あなたがOption<Vec<u32>>に種類Option<u32>の項目の上にイテレータを集めることができ、ということを。これはまさにあなたが望むものです!だから、あなたの戻り値の型を変更します。

fn parse(string: &str, lookup: &HashMap<String, u32, FastHasher>) -> Option<Vec<u32>> { 
//                 ^^^^^^^^^^^^^^^^ 
    string.split(":").map(|s| lookup.get(s).cloned()).collect() 
} 

あなたが作業コードhere on playgroundを試すことができます。


はまた、私が作った、次の質問に依存しない変化に注意してください。

  • string引数ではなく&Stringの今&strです。実際には&strの代わりに&Stringを渡す理由はないので、後者はより一般的であるために好ましい。
  • closure引数の明示的な型の注釈は必要ありません。
  • HashMapへの参照を渡したいと思うかもしれません。機能はそれを所有する必要はないからです。