I'm using the regex crate to find some text with this regex:
lazy_static! {
static ref FIND_STEPS_RE: Regex =
Regex::new(r"my regex").unwrap();
}
I want to find all possible captures and iterate over them:
FIND_STEPS_RE.captures_iter(script_slice)
Each captured element consists of 2 values: an operation and a number. For example, the output could be:
[("+", "10"), ("-", "20"), ("*", "2")]
I want to iterate over it, parse the numbers and apply the operation.
I tried:
let e = FIND_STEPS_RE.captures_iter(script_slice)
.fold(0, |sum, value| apply_decoding_step)?;
where apply_decoding_step
is:
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"),
}
}
But I got this error:
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`
I assume this is because I'm trying to fold a Result
into a i32
, but since I need to parse the second capture value and also need that otherwise
case in my match
, how can I fix that?
As jupp0r states, the initial value of Iterator::fold
must be of the same type as the return value of the closure.
You can use Iterator::try_fold
instead. This will exit iteration on the first failure:
let result = x.iter().try_fold(0, |acc, &i| apply_decoding_step(acc, i));
Complete example:
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(()),
}
}
I'd recommend using Result::and_then
to skip nothing in the fold when an error has occurred:
let result = x.iter().fold(Ok(0), |acc, &i| {
acc.and_then(|acc| apply_decoding_step(acc, i))
});
The problem here is that the fold
body is executed for every element in the iterator, even once an error occurs.
Here's an enterprise-grade solution where the main benefit is that iteration will end as soon as the first Err
is encountered, instead of spinning through the rest of the list. Secondary benefits include the ability to write very fine-grained tests for each piece (parsing from a string, arithmetic operations, accumulation, etc.):
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)),
)
}
}
Take a closer look at the type signature for fold
:
fn fold<B, F>(self, init: B, f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{ ... }
init
has to have the same type as the return value of f
. This is also what the compiler tells you in the error message. You could do
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"),
}
}
}
}
And then call it with an Ok
seed:
.fold(Ok(seed), apply_decoding_step);
Now, if any failure occurs, your fold
returns an Err
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With