6

我正在使用 regex crate 用这个 regex 查找一些文本:

lazy_static! {
    static ref FIND_STEPS_RE: Regex =
        Regex::new(r"my regex").unwrap();
}

我想找到所有可能的捕获并遍历它们:

FIND_STEPS_RE.captures_iter(script_slice)

每个捕获的元素由 2 个值组成:一个操作和一个数字。例如,输出可能是:

[("+", "10"), ("-", "20"), ("*", "2")]

我想迭代它,解析数字并应用操作。

我试过了:

let e = FIND_STEPS_RE.captures_iter(script_slice)
    .fold(0, |sum, value| apply_decoding_step)?;

哪里apply_decoding_step是:

fn apply_decoding_step(sum: i32, capture: regex::Captures<>) -> Result<i32> {
    let number = parse_number(&capture[2])?;

    match  &capture[1] {
        "+" => Ok(s + number),
        "-" => Ok(s - number),
        "*" => Ok(s * number),
        "/" => Ok(s / number),
        _ => bail!("Unknown step operator"),
    }
}

但我得到了这个错误:

error[E0271]: type mismatch resolving `<fn(i32, regex::Captures<'_>) -> std::result::Result<i32, Error> {apply_decoding_step} as std::ops::FnOnce<(i32, regex::Captures<'_>)>>::Output == i32`
   --> src/main.rs:122:10
    |
122 |         .fold(seed, apply_decoding_step);
    |          ^^^^ expected enum `std::result::Result`, found i32
    |
    = note: expected type `std::result::Result<i32, Error>`
               found type `i32`

我认为这是因为我试图将 a 折叠Result成 a i32,但由于我需要解析第二个捕获值并且还需要otherwise在我的情况下,我match该如何解决这个问题?

4

3 回答 3

11

正如jupp0r 所说, 的初始值Iterator::fold必须与闭包的返回值具有相同的类型。

锈 1.26

你可以Iterator::try_fold改用。这将在第一次失败时退出迭代:

let result = x.iter().try_fold(0, |acc, &i| apply_decoding_step(acc, i));

完整示例:

fn main() {
    let x = [("+", "10"), ("-", "20"), ("*", "2")];

    let result = x.iter().try_fold(0, |acc, &i| apply_decoding_step(acc, i));

    println!("{:?}", result);
}

fn apply_decoding_step(sum: i32, capture: (&str, &str)) -> Result<i32, ()> {
    let number: i32 = capture.1.parse().expect("nope");

    match capture.0 {
        "+" => Ok(sum + number),
        "-" => Ok(sum - number),
        "*" => Ok(sum * number),
        "/" => Ok(sum / number),
        _ => Err(()),
    }
}

锈 1.0

Result::and_then发生错误时,我建议使用在折叠中不跳过任何内容:

let result = x.iter().fold(Ok(0), |acc, &i| {
    acc.and_then(|acc| apply_decoding_step(acc, i))
});

这里的问题是fold迭代器中的每个元素都会执行主体,即使发生错误也是如此。

这是一个企业级解决方案,其主要好处是迭代将在Err遇到第一个时立即结束,而不是遍历列表的其余部分。次要好处包括能够为每个部分编写非常细粒度的测试(从字符串解析、算术运算、累加等):

fn main() {
    let x = [("+", "10"), ("-", "20"), ("*", "2")];

    let result: Result<Accumulator, ()> = x
        .iter()
        .map(|&(op, val)| {
            let op = op.parse::<Op>()?;
            let val = val.parse::<i32>().map_err(|_| ())?;
            Ok((op, val))
        })
        .collect();

    println!("{:?}", result);
}

use std::iter::FromIterator;
use std::str::FromStr;

#[derive(Debug)]
enum Op {
    Add,
    Sub,
    Mul,
    Div,
}

impl Op {
    fn apply(&self, a: i32, b: i32) -> i32 {
        use Op::*;

        match *self {
            Add => a + b,
            Sub => a - b,
            Mul => a * b,
            Div => a / b,
        }
    }
}

impl FromStr for Op {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, ()> {
        use Op::*;

        match s {
            "+" => Ok(Add),
            "-" => Ok(Sub),
            "*" => Ok(Mul),
            "/" => Ok(Div),
            _ => Err(()),
        }
    }
}

#[derive(Debug)]
struct Accumulator(i32);

impl<'a> FromIterator<(Op, i32)> for Accumulator {
    fn from_iter<I>(iter: I) -> Self
    where
        I: IntoIterator<Item = (Op, i32)>,
    {
        Accumulator(
            iter.into_iter()
                .fold(0, |acc, (op, val)| op.apply(acc, val)),
        )
    }
}
于 2017-11-15T22:38:08.057 回答
3

仔细查看 的类型签名fold

fn fold<B, F>(self, init: B, f: F) -> B
 where
    F: FnMut(B, Self::Item) -> B,
{ ... }

init必须与 的返回值具有相同的类型f。这也是编译器在错误消息中告诉您的内容。你可以做

fn apply_decoding_step(sum: Result<i32>, capture: regex::Captures<>) -> Result<i32> {
    match sum {
        Err(_) => sum,
        Ok(s) => {      
            let number = parse_number(&capture[2])?;
            match  &capture[1] {
                "+" => Ok(s + number),
                "-" => Ok(s - number),
                "*" => Ok(s * number),
                "/" => Ok(s / number),
                _ => bail!("Unknown step operator"),
           }
       }
    }
}

然后用Ok种子调用它:

.fold(Ok(seed), apply_decoding_step);

现在,如果发生任何故障,您fold将返回一个Err.

于 2017-11-15T21:49:57.313 回答
0

您可以Iterator通过这样的自定义fold_result函数进行扩展(使用完整路径,Result因为您似乎正在导入由Result生成的类型error_chain):

trait IterExtFoldResult: Iterator + Sized {
    #[inline]
    fn fold_result<B, F, E>(self, mut init: B, mut f: F) -> ::std::result::Result<B, E>
    where
        F: FnMut(B, Self::Item) -> ::std::result::Result<B, E>,
    {
        for i in self {
            init = f(init, i)?;
        }
        Ok(init)
    }
}
impl<I: Iterator> IterExtFoldResult for I {}

并像.fold_result(0, apply_decoding_step).

这种方式在返回错误fold时中止;f如果您在f编译器中转发错误可能会或可能不会优化到早期返回。

操场上的完整示例

于 2017-11-15T22:44:04.883 回答