使用Iterator时可识别类型的Map

时间:2018-12-06 09:39:05

标签: functional-programming rust

我有以下代码:

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

Rust Playground

在行.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,但似乎无法在此链中使用。

2 个答案:

答案 0 :(得分:1)

我真的不能说出确切的意思,但是-根据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,依靠FromIteryou can convert an iterator over Results into a Result

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

  • 输入不必是Vec,可以是Iterator上的String
  • 我们可以通过将单个String转换为Result<String, bool>或将某些Result<String, ErrorType>转换为ErrorType的转换函数进行参数化。
  • 您不一定需要Vec作为成功的返回类型,而是任何实现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")
            }
        })
    );
}

答案 1 :(得分:0)

您可以将结果收集在中间容器中,然后从那里处理所有错误(请注意,您将需要更改函数的返回值):

fn internal_eval(&mut self, tokens: Vec<String>) -> Result<(), Error> {
    tokens
        // Use `into_iter` to iterate over owned objects
        .into_iter()
        .map(|token| parse_token(token))
        .collect::<Result<Vec<_>, _>>()?
        .into_iter()
        .map(|token1| do_something_with_token1(token1))
        .fold(some_init, |acc, x| {
            // reduce
        });
    // ...
}

如果您无力创建一个中间容器,也可以这样做(可读性稍差):

fn internal_eval(&mut self, tokens: Vec<String>) -> Result<(), Error> {
    tokens
        .into_iter()
        .map(|token| parse_token(token))
        .map(|token1_res| token1_res.map(|token1| do_something_with_token1(token1) ))
        .try_fold(some_init, |acc, x| -> Result<SomeType, Error> {
            let val = x??;
            // ...
        });
    Ok(())
}

更新

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