在闭包中使用问号

3
我有一个简单的解析函数。
use std::collections::BTreeMap;

fn parse_kv(data: &str) -> BTreeMap<String, String> {
    data.split('&')
        .map(|kv| kv.split('='))
        .map(|mut kv| (kv.next().unwrap().into(), kv.next().unwrap().into()))
        .collect()
}

#[test]
fn parse_kv_test() {
    let result = parse_kv("test1=1&test2=2");
    assert_eq!(result["test1"], "1");
    assert_eq!(result["test2"], "2");
}


它运行良好,但我希望像这样具有选项或结果返回类型:
fn parse_kv(data: &str) -> Option<BTreeMap<String, String>>

这个实现:
fn parse_kv(data: &str) -> Option<BTreeMap<String, String>> {
    Some(data.split('&')
        .map(|kv| kv.split('='))
        .map(|mut kv| (kv.next()?.into(), kv.next()?.into()))
        .collect())
}

很遗憾,出现了以下错误:
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
  --> src/ecb_cut_paste.rs:23:24
   |
23 |         .map(|mut kv| (kv.next()?.into(), kv.next()?.into()))
   |                        ^^^^^^^^^^ cannot use the `?` operator in a function that returns `(_, _)`
   |
   = help: the trait `std::ops::Try` is not implemented for `(_, _)`
   = note: required by `std::ops::Try::from_error`

在闭包中是否可以使用 ? 操作符来从该函数返回 None?如果不能,我需要如何处理这种情况?


1
不完全是,因为我在?运算符之后链接了其他函数。 - undefined
2
我不同意这是一个重复的问题(尽管肯定有关联)。首先,当那个其他问题被发布和回答时,? 运算符并不存在。此外,经过快速浏览,我很难看出如何将其他问题的思路轻松应用到这个问题上。我认为社区会受益于对这个问题重新回答。 - undefined
1个回答

7
问题在于闭包本身就是一个函数,因此使用?会从闭包中返回而不是外部函数。然而,这仍然可以用来按照您想要的方式实现该函数:
use std::collections::BTreeMap;

fn parse_kv(data: &str) -> Option<BTreeMap<String, String>> {
    data.split('&')
        .map(|kv| kv.split('='))
        .map(|mut kv| Some((kv.next()?.into(), kv.next()?.into())))
        .collect()
}

#[test]
fn parse_kv_test() {
    let result = parse_kv("test1=1&test2=2").unwrap();
    assert_eq!(result["test1"], "1");
    assert_eq!(result["test2"], "2");

    let result2 = parse_kv("test1=1&test2");
    assert_eq!(result2, None);
}

需要翻译的内容:

这里有几点需要注意:首先,第二个map调用中的问号和Some(...)意味着您拥有一个Option<(String, String)>的迭代器 - 类型推断会为您解决这个问题。

下一个需要注意的点是collect()可以自动将Iterator<Option<T>>转换为Option<Collection<T>>(同样适用于Result - 相关文档在此处)。我添加了一个演示它有效的测试。

还有一件需要注意的事情是,使用collect这种方式仍然允许短路。一旦迭代器产生第一个Nonecollect将立即返回None,而不是继续处理每个元素。


太棒了,这就是我知道的那种美感,但我真的无法理解。谢谢! - undefined
@LeśnyRumcajs 很高兴能帮到你! - undefined
如果你使用for_each而不是map,并且不需要收集结果,你会如何实现这个功能? - undefined
在这种情况下,我会使用普通的for循环,因为它们适用于任何迭代器。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接