In this function parse
can return an error so I use .filter_map(Result::ok)
to filter them out.
fn part1(input: &str) {
let sum = input.lines()
.map(|l| l.parse::<u32>())
.filter_map(Result::ok)
.map(|n| n as f32 / 3.0)
.map(|f| f.round())
.map(|f| f as u32 - 2)
.sum::<u32>();
// println!("{}", sum);
println!("{:?}", sum);
}
However, I would like to return out of the part1
function when parse gives an error, kind of like using the question mark operator like this .map(|l| l.parse::<u32>()?)
. If this is done the compiler gives the error
error[E0277]: the `?` operator can only be used in a closure that returns `Result`
or `Option` (or another type that implements `std::ops::Try`)
--> src/main.rs:64:18
|
64 | .map(|l| l.parse::<u32>()?)
| ----^^^^^^^^^^^^^^^^^
| | |
| | cannot use the `?` operator in a closure that returns `u32`
| this function should return `Result` or `Option` to accept `?`
Is this because the question mark operator is used inside a closure so it returns out of the closure instead of the enclosing function? What are some idiomatic alternatives to using the question mark operator inside the closure so that I can return out of part1
if parse
gives an error or unwrap the Ok
if parse is successful? The result should be similar to .filter_map(Result::ok)
, except instead of filtering out the errors it will return out of the enclosing function when there is an error.
You can just keep passing the Result
from parse
further down the chain and allow the final sum
to work - since Sum
is implemented for Result
. Then you can use ?
on the final result of the chain.
An example would look like this:
fn part1(input: &str) -> Result<u32,std::num::ParseIntError> {
let sum = input.lines()
.map(|l| l.parse::<u32>())
.map(|n| n.map( |n| n as f32 / 3.0) )
.map(|f| f.map( |f| f.round() ) )
.map(|f| f.map( |f| f as u32 - 2) )
.sum::<Result<u32,_>>()?;
Ok(sum)
}
If you're using nightly rust you can get rid of the nested closures using a try block
#![feature(try_blocks)]
fn part1(input: &str) -> Result<u32, std::num::ParseIntError> {
let sum = input.lines()
.map( |l| try {
let n = l.parse::<u32>()?;
let f = n as f32 / 3.0;
let f = f.round();
f as u32 - 2
})
.sum::<Result<u32,_>>()?;
Ok(sum)
}
If you are not using nightly you can extract the processing into a closure that returns a Result.
fn part1(input: &str) -> Result<u32, std::num::ParseIntError> {
let process_line = |l:&str| -> Result<u32,std::num::ParseIntError> {
let n = l.parse::<u32>()?;
let f = n as f32 / 3.0;
let f = f.round();
Ok(f as u32 - 2)
};
let sum = input.lines().map(process_line).sum::<Result<u32,_>>()?;
Ok(sum)
}
I'm also assuming that your real use case is somewhat more complicated than you've presented here. For something this simple I'd just use a for loop
fn part1(input: &str) -> Result<u32,std::num::ParseIntError> {
let mut sum = 0;
for line in input.lines() {
let n = l.parse::<u32>()?;
let f = n as f32 / 3.0;
let f = f.round();
sum += f as u32 - 2;
}
Ok(sum)
}
The multiple calls to map
might make some solutions feel cluttered.
Instead, all your math could be performed in a single call to map
, that is then used with sum
:
fn part1(input: &str) -> Result<(), std::num::ParseIntError> {
let sum = input.lines()
.map(|l| {
let n = l.parse::<u32>()?;
let mut f = n as f32 / 3.0;
f = f.round();
Ok(f as u32 - 2)
})
.sum::<Result<u32, _>>()?;
// println!("{}", sum);
println!("{:?}", sum);
Ok(())
}
But you could then go further by removing the ?
and using map
on the Result
. If you do this along with returning a value from your function, you don't even need the explicit type parameter to sum
:
fn part1(input: &str) -> Result<u32, std::num::ParseIntError> {
input.lines()
.map(|l| {
l.parse::<u32>().map(|n| {
let mut f = n as f32 / 3.0;
f = f.round();
f as u32 - 2
})
})
.sum()
}
You would then have to call println
outside of the function.
If you don't like the nested closures, you can always extract the math to another function (with a better name):
fn part1(input: &str) -> Result<u32, std::num::ParseIntError> {
input.lines()
.map(|l| l.parse().map(math_part))
.sum()
}
fn math_part(n: u32) -> u32 {
let mut f = n as f32 / 3.0;
f = f.round();
f as u32 - 2
}
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