Use map object as function input - rust

I have a function that calculates the variance of an iterator of floats, I would like to be able to call this function on an iterator after using the map method to transform it.
use num::Float;
fn compute_var_iter<'a, I, T>(vals: I) -> T
where
I: Iterator<Item = &'a T>,
T: 'a + Float + std::ops::AddAssign,
{
// online variance function
// Var = E[X^2] - E[X]^2
// corrects for 1/n -> 1/(n-1)
let mut x = T::zero();
let mut xsquare = T::zero();
let mut len = T::zero();
for &val in vals {
x += val;
xsquare += val * val;
len += T::one();
}
((xsquare / len) - (x / len) * (x / len)) / (len - T::one()) * len
}
fn main() {
let a: Vec<f64> = (1..100001).map(|i| i as f64).collect();
let b: Vec<f64> = (0..100000).map(|i| i as f64).collect();
dbg!(compute_var_iter(&mut a.iter())); // this works
dbg!(compute_var_iter(a.iter().zip(b).map(|(x, y)| x * y))); // this does not work
}
Is there a performant way to get the map output back to an iterator or to make the function take the map object as an input so that we can avoid having to .collect() and keep the execution lazy?

You can use the iterator objects directly without collect:
use num::Float;
fn compute_var_iter<I, T>(vals: I) -> T
where
I: Iterator<Item = T>,
T: Float + std::ops::AddAssign,
{
// online variance function
// Var = E[X^2] - E[X]^2
// corrects for 1/n -> 1/(n-1)
let mut x = T::zero();
let mut xsquare = T::zero();
let mut len = T::zero();
for val in vals {
x += val;
xsquare += val * val;
len += T::one();
}
((xsquare / len) - (x / len) * (x / len)) / (len - T::one()) * len
}
fn main() {
let a = (1..100001).map(|i| i as f64);
let b = (0..100000).map(|i| i as f64);
let c: Vec<f64> = (0..10000).map(|i| i as f64).collect();
dbg!(compute_var_iter(a.clone())); // this works
dbg!(compute_var_iter(c.iter().map(|i| *i))); // this works
dbg!(compute_var_iter(a.zip(b).map(|(x, y)| x * y)));
}
Playground
Notice that you would need to clone the iterator if you intend to use it several times. Also you do not really need to use references since numbers are usually Copy and the cost is the same as creating the references itself.

Related

How to properly initialize a struct in Rust, with good enough encapsulations?

How to properly initialize a struct in Rust, with good enough encapsulations?
Or more naively:
how to leverage object/instance methods in the initialization/constructing process of structs?
For example, as the initialization block in Kotlin:
private class BinaryIndexedTree(nums: IntArray) {
private val nNums = nums.size
private val fenwick = IntArray(nNums + 1) { 0 }
// where to put this block in Rust?
init {
for (idx in nums.indices) {
update(idx, nums[idx])
}
}
fun update(index: Int, value: Int) {
var idx = index + 1
while (idx <= nNums) {
fenwick[idx] += value
idx += (idx and -idx)
}
}
fun query(index: Int): Int {
var sum = 0
var idx = index + 1
while (idx > 0) {
sum += fenwick[idx]
idx -= (idx and -idx)
}
return sum
}
}
According to Rust Design Patterns, there is no regular constructors as other languages, the convention is to use an associated function.
Correspondingly, in Rust:
struct BinaryIndexedTree{
len_ns: isize,
fenwick: Vec<i32>,
}
impl BinaryIndexedTree{
pub fn new(nums: &Vec<i32>) -> Self{
let len_ns: usize = nums.len();
let fenwick: Vec<i32> = vec![0; len_ns + 1];
for (idx, num) in nums.iter().enumerate(){
// how to leverage `update()` for initialization
// update(idx as isize, num);
// or even earlier: where/how to put the initialization logic?
}
Self{
len_ns: len_ns as isize,
fenwick,
}
}
pub fn update(&mut self, index: isize, value: i32){
let mut idx = index + 1;
while idx <= self.len_ns{
self.fenwick[idx as usize] += value;
idx += (idx & -idx);
}
}
pub fn query(&self, index: isize) -> i32{
let mut sum: i32 = 0;
let mut idx = index + 1;
while idx > 0{
sum += self.fenwick[idx as usize];
idx -= (idx & -idx);
}
sum
}
}
Is there any way to properly leverage the update method?
As a rule of thumbs, how to properly handle the initialization work after the creation of (all the fields of) the struct?
The builder pattern is a way to go, which introduces much more code just for initialization.
Yes, you can construct the struct then call a function on it before returning it. There is nothing special about the new function name or how the struct is constructed at the end of the function.
pub fn new(nums: &Vec<i32>) -> Self {
let len_ns: usize = nums.len();
let fenwick: Vec<i32> = vec![0; len_ns + 1];
// Construct an incomplete version of the struct.
let mut new_self = Self {
len_ns: len_ns as isize,
fenwick,
};
// Do stuff with the struct
for (idx, num) in nums.iter().enumerate(){
new_self.update(idx as isize, num);
}
// Return it
new_self
}

How to declare generic types for a function that computes k-shortest-paths using Yen's algorithm and petgraph?

I have implemented Yen's algorithm Wikipedia using petgraph in Rust.
In a main function, the code looks like this:
use std::collections::BinaryHeap;
use std::cmp::Reverse;
use std::collections::HashSet;
use petgraph::{Graph, Undirected};
use petgraph::graph::NodeIndex;
use petgraph::stable_graph::StableUnGraph;
use petgraph::algo::{astar};
use petgraph::visit::NodeRef;
fn main() {
let mut graph: Graph<String, u32, Undirected> = Graph::new_undirected();
let c = graph.add_node(String::from("C"));
let d = graph.add_node(String::from("D"));
let e = graph.add_node(String::from("E"));
let f = graph.add_node(String::from("F"));
let g = graph.add_node(String::from("G"));
let h = graph.add_node(String::from("H"));
graph.add_edge(c, d, 3);
graph.add_edge(c, e, 2);
graph.add_edge(d, e, 1);
graph.add_edge(d, f, 4);
graph.add_edge(e, f, 2);
graph.add_edge(e, g, 3);
graph.add_edge(f, g, 2);
graph.add_edge(f, h, 1);
graph.add_edge(g, h, 2);
let start = c;
let goal = h;
// start solving Yen's k-shortest-paths
let (length, path) = match astar(&graph, start, |n| n == goal.unwrap(), |e| *e.weight(), |_| 0) {
Some(x) => x,
None => panic!("Testing!"),
};
println!("Initial path found\tlength: {}", length);
for i in 0..(path.len() - 1) {
println!("\t{:?}({:?}) -> {:?}({:?})", graph.node_weight(path[i].id()).unwrap(), path[i].id(), graph.node_weight(path[i+1].id()).unwrap(), path[i+1].id());
}
let k = 10;
let mut ki = 0;
let mut visited = HashSet::new();
let mut routes = vec![(length, path)];
let mut k_routes = BinaryHeap::new();
for ki in 0..(k - 1) {
println!("Computing path {}", ki);
if routes.len() <= ki {
// We have no more routes to explore
break;
}
let previous = routes[ki].1.clone();
for i in 0..(previous.len() - 1) {
let spur_node = previous[i].clone();
let root_path = &previous[0..i];
let mut graph_copy = StableUnGraph::<String, u32>::from(graph.clone());
println!("\tComputing pass {}\tspur: {:?}\troot: {:?}", i, graph.node_weight(spur_node), root_path.iter().map(|n| graph.node_weight(*n).unwrap()));
for (_, path) in &routes {
if path.len() > i + 1 && &path[0..i] == root_path {
let ei = graph.find_edge_undirected(path[i], path[i + 1]);
if ei.is_some() {
let edge = ei.unwrap().0;
graph_copy.remove_edge(edge);
let edge_obj = graph.edge_endpoints(edge);
let ns = edge_obj.unwrap();
println!("\t\tRemoving edge {:?} from {:?} -> {:?}", edge, graph.node_weight(ns.0).unwrap(), graph.node_weight(ns.1).unwrap());
}
else {
panic!("\t\tProblem finding edge");
}
}
}
if let Some((_, spur_path)) =
astar(&graph_copy, spur_node, |n| n == goal.unwrap(), |e| *e.weight(), |_| 0)
{
let nodes: Vec<NodeIndex> = root_path.iter().cloned().chain(spur_path).collect();
let mut node_names = vec![];
for ni in 0..nodes.len() {
node_names.push(graph.node_weight(nodes[ni]).unwrap());
}
// compute root_path length
let mut path_length = 0;
for i_rp in 0..(nodes.len() - 1) {
let ei = graph.find_edge_undirected(nodes[i_rp], nodes[i_rp + 1]);
if ei.is_some() {
let ew = graph.edge_weight(ei.unwrap().0);
if ew.is_some() {
path_length += ew.unwrap();
}
}
}
println!("\t\t\tfound path: {:?} with cost {}", node_names, path_length);
if !visited.contains(&nodes) {
// Mark as visited
visited.insert(nodes.clone());
// Build a min-heap
k_routes.push(Reverse((path_length, nodes)));
}
}
}
if let Some(k_route) = k_routes.pop() {
println!("\tselected route {:?}", k_route.0);
routes.push(k_route.0);
}
}
}
Now, I want to put this algorithm within a function that I can call from my code. I made an initial attempt with the signature like this:
pub fn yen_k_shortest_paths<G, E, Ty, Ix, F, K>(
graph: Graph<String, u32, Undirected>,
start: NodeIndex<u32>,
goal: NodeIndex<u32>,
mut edge_cost: F,
k: usize,
) -> Result<Vec<(u32, Vec<NodeIndex<u32>>)>, Box<dyn std::error::Error>>
where
G: IntoEdges + Visitable,
Ty: EdgeType,
Ix: IndexType,
E: Default + Debug + std::ops::Add,
F: FnMut(G::EdgeRef) -> K,
K: Measure + Copy,
{
// implementation here
}
However, when I try to call the function with:
let paths = yen::yen_k_shortest_paths(graph, start, goal, |e: EdgeReference<u32>| *e.weight(), 5);
the compiler complains: type annotations needed cannot satisfy <_ as IntoEdgeReferences>::EdgeRef == petgraph::graph::EdgeReference<'_, u32>`
I already tried several alternatives without success. Do you have any suggestion on how to fix this issue?
The issue with the yen_k_shortest_paths() function signature as written is the generic type parameters aren't used correctly. As an example, consider the first declared type parameter on yen_k_shortest_paths(): G, which is intended to represent the graph type. Declaring G like this means that the code that calls yen_k_shortest_paths() gets to pick the graph type G. But the graph argument is declared with the concrete type Graph<String, u32, Undirected>—the caller has no choice. This contradiction is the problem with G. Similar reasoning applies to the other type parameters, except F and K. There are two ways to fix this kind of issue:
Keep the graph argument as Graph<String, u32, Undirected> and remove the G type parameter.
Change the graph argument to take a G.
Approach #1 is simpler but your function won't be as general. Approach #2 can involve needing to add extra bounds and some code changes in the function in order for the code to compile.
In this case, the simplest approach doesn't need any type parameters at all:
fn yen_k_shortest_paths(
graph: &Graph<String, u32, Undirected>,
start: NodeIndex<u32>,
goal: NodeIndex<u32>,
edge_cost: fn(EdgeReference<u32>) -> u32,
k: usize,
) -> Vec<(u32, Vec<NodeIndex<u32>>)> {...}
Here's the full code, which can be run:
use std::cmp::Reverse;
use std::collections::BinaryHeap;
use std::collections::HashSet;
use petgraph::algo::astar;
use petgraph::graph::{EdgeReference, NodeIndex};
use petgraph::stable_graph::StableUnGraph;
use petgraph::visit::NodeRef;
use petgraph::{Graph, Undirected};
fn main() {
let mut graph: Graph<String, u32, Undirected> = Graph::new_undirected();
let c = graph.add_node(String::from("C"));
let d = graph.add_node(String::from("D"));
let e = graph.add_node(String::from("E"));
let f = graph.add_node(String::from("F"));
let g = graph.add_node(String::from("G"));
let h = graph.add_node(String::from("H"));
graph.add_edge(c, d, 3);
graph.add_edge(c, e, 2);
graph.add_edge(d, e, 1);
graph.add_edge(d, f, 4);
graph.add_edge(e, f, 2);
graph.add_edge(e, g, 3);
graph.add_edge(f, g, 2);
graph.add_edge(f, h, 1);
graph.add_edge(g, h, 2);
let start = c;
let goal = h;
let edge_cost = |e: EdgeReference<u32>| *e.weight();
let k = 10;
let _paths = yen_k_shortest_paths(&graph, start, goal, edge_cost, k);
}
fn yen_k_shortest_paths(
graph: &Graph<String, u32, Undirected>,
start: NodeIndex<u32>,
goal: NodeIndex<u32>,
edge_cost: fn(EdgeReference<u32>) -> u32,
k: usize,
) -> Vec<(u32, Vec<NodeIndex<u32>>)> {
let (length, path) = match astar(graph, start, |n| n == goal, edge_cost, |_| 0) {
Some(x) => x,
None => panic!("Testing!"),
};
println!("Initial path found\tlength: {}", length);
for i in 0..(path.len() - 1) {
println!(
"\t{:?}({:?}) -> {:?}({:?})",
graph.node_weight(path[i].id()).unwrap(),
path[i].id(),
graph.node_weight(path[i + 1].id()).unwrap(),
path[i + 1].id()
);
}
let mut visited = HashSet::new();
let mut routes = vec![(length, path)];
let mut k_routes = BinaryHeap::new();
for ki in 0..(k - 1) {
println!("Computing path {}", ki);
if routes.len() <= ki {
// We have no more routes to explore
break;
}
let previous = routes[ki].1.clone();
for i in 0..(previous.len() - 1) {
let spur_node = previous[i];
let root_path = &previous[0..i];
let mut graph_copy = StableUnGraph::from(graph.clone());
println!(
"\tComputing pass {}\tspur: {:?}\troot: {:?}",
i,
graph.node_weight(spur_node),
root_path
.iter()
.map(|n| graph.node_weight(*n).unwrap())
.collect::<Vec<_>>()
);
for (_, path) in &routes {
if path.len() > i + 1 && &path[0..i] == root_path {
let ei = graph.find_edge_undirected(path[i], path[i + 1]);
if let Some(ei) = ei {
let edge = ei.0;
graph_copy.remove_edge(edge);
let edge_obj = graph.edge_endpoints(edge);
let ns = edge_obj.unwrap();
println!(
"\t\tRemoving edge {:?} from {:?} -> {:?}",
edge,
graph.node_weight(ns.0).unwrap(),
graph.node_weight(ns.1).unwrap()
);
} else {
panic!("\t\tProblem finding edge");
}
}
}
if let Some((_, spur_path)) = astar(
&graph_copy,
spur_node,
|n| n == goal,
|e| *e.weight(),
|_| 0,
) {
let nodes: Vec<NodeIndex> = root_path.iter().cloned().chain(spur_path).collect();
let mut node_names = vec![];
for &node in &nodes {
node_names.push(graph.node_weight(node).unwrap());
}
// compute root_path length
let mut path_length = 0;
for i_rp in 0..(nodes.len() - 1) {
let ei = graph.find_edge_undirected(nodes[i_rp], nodes[i_rp + 1]);
if let Some(ei) = ei {
let ew = graph.edge_weight(ei.0);
if let Some(&ew) = ew {
path_length += ew;
}
}
}
println!(
"\t\t\tfound path: {:?} with cost {}",
node_names, path_length
);
if !visited.contains(&nodes) {
// Mark as visited
visited.insert(nodes.clone());
// Build a min-heap
k_routes.push(Reverse((path_length, nodes)));
}
}
}
if let Some(k_route) = k_routes.pop() {
println!("\tselected route {:?}", k_route.0);
routes.push(k_route.0);
}
}
routes
}
As another example of a possible function signature, this one is generic over the node type N and the edge cost function F:
fn yen_k_shortest_paths<'a, N, F>(
graph: &'a Graph<N, u32, Undirected>,
start: NodeIndex<u32>,
goal: NodeIndex<u32>,
edge_cost: F,
k: usize,
) -> Vec<(u32, Vec<NodeIndex<u32>>)>
where
&'a Graph<N, u32, Undirected>:
GraphBase<NodeId = NodeIndex<u32>> + IntoEdgeReferences<EdgeRef = EdgeReference<'a, u32>>,
N: Debug + Clone,
F: FnMut(EdgeReference<u32>) -> u32,
{...}
As you can see, these bounds can get pretty complicated. Figuring them out involved reading the error messages the compiler emitted, as well as reading the docs for the involved types/traits. (Although, I think in this case the complicated bound &'a Graph<N, u32, Undirected>: GraphBase<NodeId = NodeIndex<u32>> + IntoEdgeReferences<EdgeRef = EdgeReference<'a, u32>> should be inferred, but currently isn't due to a complier bug/limitation)

Chaining iterators together to remove repeated code

The following code attempts to chain two iterators together.
fn main() {
let height = 3;
let width = 4;
let horizontal = (0..height).map(|row| {let rw = row * width; rw..rw + width});
horizontal.for_each(|x| { print!("("); x.for_each(|x|print!(" {:?} ", x)); println!(")");});
let vertical = (0..width).map(|col| (0..height).map(move |n| col + n * width));
vertical.for_each(|x| { print!("("); x.for_each(|x|print!(" {:?} ", x)); println!(")");});
let all = horizontal.chain(vertical);
//all.for_each(|x| { print!("("); x.for_each(|x|print!(" {:?} ", x)); println!(")");});
}
But the compiler complains about mismatched types.
error[E0271]: type mismatch resolving `<Map<std::ops::Range<{integer}>, [closure#src/main.rs:6:35: 6:82]> as IntoIterator>::Item == std::ops::Range<{integer}>`
--> src/main.rs:8:26
|
8 | let all = horizontal.chain(vertical);
| ^^^^^ expected struct `Map`, found struct `std::ops::Range`
|
= note: expected type `Map<std::ops::Range<{integer}>, [closure#src/main.rs:6:57: 6:81]>`
found struct `std::ops::Range<{integer}>`
The signature of chain is:
fn chain<U>(self, other: U) -> Chain<Self, <U as IntoIterator>::IntoIter>ⓘ where
U: IntoIterator<Item = Self::Item>
Both iterators have as Item type an Iterator with the same Item type, which admittedly is not quite what the signature demands. But I can call for example .for_each(|x| { print!("("); x.for_each(|x|print!(" {:?} ", x)); println!(")");}) on each iterator, so why can't I construct the chain to call it on the chain? Is there another way to remove such code duplication?
It's because your types doesn't have the same Item:
.map(|row| {let rw = row * width; rw..rw + width});
.map(|col| (0..height).map(move |n| col + n * width))
One is a Range the other is a Map of a Range.
The solution is to use flatten() or on your case flat_map():
fn main() {
let height = 3;
let width = 4;
println!("Horizontal:");
let horizontal = (0..height).flat_map(|row| {
let rw = row * width;
rw..rw + width
});
for x in horizontal.clone() {
println!("{:?}", x);
}
println!("\nVertical:");
let vertical = (0..width).flat_map(|col| (0..height).map(move |n| col + n * width));
for x in vertical.clone() {
println!("{:?}", x);
}
println!("\nAll:");
let all = horizontal.chain(vertical);
for x in all {
println!("{:?}", x);
}
}
This made both vertical and horizontal iterator have the same Item type. Also, I remove for_each() in my opinion it's make the code unclear as for loop are for side effect that is imperative paradigm and iterator chaining is functional paradigm.
Bonus:
fn print_my_iter(name: &str, iter: impl Iterator<Item = i32>) {
println!("{}:", name);
for x in iter {
println!("{:?}", x);
}
}
fn main() {
let height = 3;
let width = 4;
let horizontal = (0..height).flat_map(|row| {
let rw = row * width;
rw..rw + width
});
print_my_iter("Horizontal", horizontal.clone());
let vertical = (0..width).flat_map(|col| (0..height).map(move |n| col + n * width));
print_my_iter("\nVertical", vertical.clone());
let all = horizontal.chain(vertical);
print_my_iter("\nAll", all);
}
Ultra bonus:
use std::io::{self, Write};
fn print_my_iter(name: &str, iter: impl Iterator<Item = i32>) -> Result<(), io::Error> {
let stdout = io::stdout();
let mut handle = stdout.lock();
writeln!(handle, "{}:", name)?;
iter.map(|x| writeln!(handle, "{:?}", x)).collect()
}
fn main() -> Result<(), io::Error> {
let height = 3;
let width = 4;
let horizontal = (0..height).flat_map(|row| {
let rw = row * width;
rw..rw + width
});
print_my_iter("Horizontal", horizontal.clone())?;
let vertical = (0..width).flat_map(|col| (0..height).map(move |n| col + n * width));
print_my_iter("\nVertical", vertical.clone())?;
let all = horizontal.chain(vertical);
print_my_iter("\nAll", all)?;
Ok(())
}

How to write a macro that splits a byte into a tuple of bits of user-specified count?

I would like to have macro splitting one byte into tuple with 2-8 u8 parts using bitreader crate.
I managed to achieve that by following code:
use bitreader::BitReader;
trait Tupleprepend<T> {
type ResultType;
fn prepend(self, t: T) -> Self::ResultType;
}
macro_rules! impl_tuple_prepend {
( () ) => {};
( ( $t0:ident $(, $types:ident)* ) ) => {
impl<$t0, $($types,)* T> Tupleprepend<T> for ($t0, $($types,)*) {
type ResultType = (T, $t0, $($types,)*);
fn prepend(self, t: T) -> Self::ResultType {
let ($t0, $($types,)*) = self;
(t, $t0, $($types,)*)
}
}
impl_tuple_prepend! { ($($types),*) }
};
}
impl_tuple_prepend! {
(_1, _2, _3, _4, _5, _6, _7, _8)
}
macro_rules! split_byte (
($reader:ident, $bytes:expr, $count:expr) => {{
($reader.read_u8($count).unwrap(),)
}};
($reader:ident, $bytes:expr, $count:expr, $($next_counts:expr),+) => {{
let head = split_byte!($reader, $bytes, $count);
let tail = split_byte!($reader, $bytes, $($next_counts),+);
tail.prepend(head.0)
}};
($bytes:expr $(, $count:expr)* ) => {{
let mut reader = BitReader::new($bytes);
split_byte!(reader, $bytes $(, $count)+)
}};
);
Now I can use this code as I would like to:
let buf: &[u8] = &[0x72];
let (bit1, bit2, bits3to8) = split_byte!(&buf, 1, 1, 6);
Is there a way to avoid using Tupleprepend trait and create only 1 tuple instead of 8 in the worst scenario?
Because the number of bit widths directly corresponds to the number of returned values, I'd solve the problem using generics and arrays instead. The macro only exists to remove the typing of the [], which I don't really think is worth it.
fn split_byte<A>(b: u8, bit_widths: A) -> A
where
A: Default + std::ops::IndexMut<usize, Output = u8>,
for<'a> &'a A: IntoIterator<Item = &'a u8>,
{
let mut result = A::default();
let mut start = 0;
for (idx, &width) in bit_widths.into_iter().enumerate() {
let shifted = b >> (8 - width - start);
let mask = (0..width).fold(0, |a, _| (a << 1) | 1);
result[idx] = shifted & mask;
start += width;
}
result
}
macro_rules! split_byte {
($b:expr, $($w:expr),+) => (split_byte($b, [$($w),+]));
}
fn main() {
let [bit1, bit2, bits3_to_8] = split_byte!(0b1010_1010, 1, 1, 6);
assert_eq!(bit1, 0b1);
assert_eq!(bit2, 0b0);
assert_eq!(bits3_to_8, 0b10_1010);
}
See also:
How does for<> syntax differ from a regular lifetime bound?
How to write a trait bound for adding two references of a generic type?
How do I write the lifetimes for references in a type constraint when one of them is a local reference?
If it's ok to target nightly Rust, I'd use the unstable min_const_generics feature:
#![feature(min_const_generics)]
fn split_byte<const N: usize>(b: u8, bit_widths: [u8; N]) -> [u8; N] {
let mut result = [0; N];
let mut start = 0;
for (idx, &width) in bit_widths.iter().enumerate() {
let shifted = b >> (8 - width - start);
let mask = (0..width).fold(0, |a, _| (a << 1) | 1);
result[idx] = shifted & mask;
start += width;
}
result
}
macro_rules! split_byte {
($b:expr, $($w:expr),+) => (split_byte($b, [$($w),+]));
}
fn main() {
let [bit1, bit2, bits3_to_8] = split_byte!(0b1010_1010, 1, 1, 6);
assert_eq!(bit1, 0b1);
assert_eq!(bit2, 0b0);
assert_eq!(bits3_to_8, 0b10_1010);
}
See also:
Is it possible to control the size of an array using the type parameter of a generic?

Alternatives for using the question mark operator inside a map function closure

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
}

Resources