【问题标题】:Nice way to map with potential failure [duplicate]映射潜在故障的好方法[重复]
【发布时间】:2025-12-19 15:10:10
【问题描述】:

我正在尝试解析一系列 Json 对象,这些对象可能会取消整个函数。

理想情况下,我会这样做:

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())
}

当然,try 不会在错误时逃脱map(),而不是Result&lt;Vec&lt;Video&gt;,_&gt;,我得到Vec&lt;Result&lt;Video,_&gt;&gt;。我可以将其重写为手动迭代,将元素添加到新的 vec 中,但我觉得我缺少一些更简单的处理方式。

是否有一些现有的功能可以让我轻松地从Iter&lt;Result&lt;T&gt;&gt;Result&lt;Vec&lt;T&gt;,_&gt;

【问题讨论】:

  • 我不认为第二次尝试!随心所欲地工作。尝试!会让你摆脱封闭。对了,你看过flat_map

标签: rust


【解决方案1】:

在函数式编程语言中,您可以将选项和结果视为容器,Rust 与此类似,因此您可以map / flat_map 处理它们。你可以使用flat_map 来做到这一点。如果videos 已经是一个向量,您只需针对flat_mapped 长度测试Ok 的预期数量,以决定是否返回Ok

但是,您应该尽量保持惰性,在第一次失败后不要继续解析。 take_while 是这里的一个选项。无论哪种方式,您都需要跟踪您是否一路看到parse_failure。像下面这样的工作 - 它演示了flat_map 如何丢弃Errors,但它的解析超出了必要的范围。您也可以使用.filter 然后.map 来获取解析结果

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")
    }
}

这是一个懒惰的选择 -

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

您可以致电unwrap,因为您从第一次失败开始就放弃了所有内容。现在你返回Ok 如果extracted.len() == videos.len()

【讨论】:

  • @shepmaster ...正如它所写的那样,它在第一次失败之后解析。
  • 啊,说得好!我会清除我的评论以防止任何混淆。