首页 > 解决方案 > 使用迭代器时的类型感知映射

问题描述

我有以下代码:

fn main() {
    let xs = vec!["1i32".to_string(), "2".to_string(), "3".to_string()];
    let ys = do_some(xs);
    println!("{:?}", ys);
}

fn do_some(tokens: Vec<String>) -> Result<Vec<String>, bool> {
    tokens
        .into_iter()
        .map(|token: String| Ok(token))
        .map(|token: Result<String, bool>| token.map(|s| s + "a"))
        .fold(Ok(Vec::new()), |acc, s| acc.push(s))
}

锈游乐场

在 line 之后.map(|token| Ok(token)),我希望调用map会起作用Result而不是起作用Iterator,因此能够解开Result,但我得到Result<String, bool>的输出数据是:

error[E0599]: no method named `push` found for type `std::result::Result<std::vec::Vec<std::string::String>, bool>` in the current scope
  --> src/main.rs:12:44
   |
12 |         .fold(Ok(Vec::new()), |acc, s| acc.push(s))
   |                                            ^^^^

在 Rust 中编写函数式代码的最佳方法是什么?

我知道,and_then但似乎无法在此链中使用。

标签: functional-programmingrust

解决方案


我不能真正告诉你究竟在追求什么,但是 - 从签名来看do_some- 我想你有一个Vec<String>, 一个函数正在运行String,返回一个Result<String, bool>,并且你想将此函数应用于 中的每个元素Vec,并获取它们变成一个Vec如果一切都是Ok。如果你遇到一个Err,你想返回一个Err。这可以通过以下方式实现:

fn do_some(tokens: Vec<String>) -> Result<Vec<String>, bool> {
    tokens
        .into_iter()
        .map(|token: String| Ok(token)) // or any other Fn(String)->Result<String, bool>
        .map(|token: Result<String, bool>| token.map(|s| s + "a"))
        .collect()
}

这使用collect,依赖FromIter,您可以将s上的迭代器转换ResultResult为。

请注意,您可以使所有这些更通用:

  • 输入不需要是 a Vec,它可以是一个Iteratorover Strings。
  • String我们可以通过将 single 转换为a的转换函数对其进行参数化Result<String, bool>,或者 - 更通用地 - 转换为 a Result<String, ErrorType>for some ErrorType
  • 您不一定需要 aVec作为成功的返回类型,而是需要任何实现FromIter(通常是容器类型)。

执行此操作的第一步可能如下所示:

fn do_some<
    ErrorType,
    Tokens: std::iter::IntoIterator<Item=String>, // an iterable yielding String
    StringToResult: Fn(String) -> Result<String, ErrorType>,
>(
    tokens: Tokens,
    string_to_result: StringToResult,
) -> Result<Vec<String>, ErrorType> {
    tokens
        .into_iter()
        .map(|s| string_to_result(s).map(|s| s + "a"))
        .collect()
}

可以按如下方式使用:

fn main() {
    println!("{:?}",
        do_some(vec!["i1".to_string(), "i2".to_string(), "i3".to_string()], |s| {
            if s.starts_with("i") {
                Ok(s)
            } else {
                Err(s + " i does not start with i")
            }
        })
    );
    println!("{:?}",
        do_some(vec!["i1".to_string(), "i2".to_string(), "A3".to_string()], |s| {
            if s.starts_with("i") {
                Ok(s)
            } else {
                Err(s + " i does not start with i")
            }
        })
    );
}

推荐阅读