4

I'm trying to parse a series of Json objects with potential failures that cancel the whole function.

Ideally, I'd do something like:

fn .... -> Result<Vec<Video>, YoutubeParseError> {
    ...
    let videos = try!(doc.find("items").
        and_then(Json::as_array).
        ok_or(YoutubeParseError));

    Ok(videos.into_iter().
        map(|item| try!(json_to_video(item))).
        collect())
}

But of course try doesn't escape the map() on error and instead of Result<Vec<Video>,_>, I get Vec<Result<Video,_>>. I could rewrite this as manual iteration adding elements into a new vec, but I feel like I'm missing some simpler way of handling this.

Is there some existing function that would get me from Iter<Result<T>> to Result<Vec<T>,_> easily?

viraptor
  • 33,322
  • 10
  • 107
  • 191
  • I don't think that second try! works as you want. try! will get you out of the closure. By the way have you looked at `flat_map` – sgldiv Aug 09 '16 at 03:54

1 Answers1

1

In functional programming languages you can treat options and results as containers and Rust is similar, so you can map / flat_map over them. You could do this with flat_map. If videos is already a vector, you can just test for expected number of Ok's against a flat_mapped length to decide whether to return Ok.

However, you should try to keep things lazy and not continue parsing after the first failure. take_while would be an option here. Either way, you will need to track if you saw a parse_failure along the way. Something like below works - it demonstrates how flat_map drops Errors, but it parses more than necessary. You could also use a .filter and then .map to get the parse result

fn get_videos(test: &Vec<&str>) -> Result<Vec<u32>, &'static str> {
    let videos = ...
    let expected = videos.len();
    let extracted = v.into_iter().flat_map(|x| json_to_video(x)).collect();
    if extracted.len() == expected {
        Ok(extracted)
    } else {
        Err("not_ok")
    }
}

Here's an option to do it lazily -

let extracted = videos.map(|x|json_to_video(x))
                      .take_while(|x|x.is_ok())
                      .map(|x|x.ok().unwrap())
                      .collect() 

You can call unwrap as you dropped everything starting at first failure. Now you return Ok if extracted.len() == videos.len()

sgldiv
  • 623
  • 6
  • 16