I would like to write some code in a "functional programming" style.
However, I start with an Iterator of Results and I only want to apply the function to the Ok items. Furthermore, I want to stop the iteration on the first error (however, I'd be open to different behavior).
So far, I am using a nested map() pattern: <iter>.map(|l| l.map(replace)). I think this is extremely ugly.
Using the nightly "result_flattening", I can flatten each nested Result<Result<T, E>, E> into a Result<T, E>. Using eyre::Context I convert the different Error types into an eyre::Report error type. All of this feels quite clumsy.
What is an elegant way to write this in Rust?
Minimal Working Example
#![feature(result_flattening)]
use std::io::BufRead;
use eyre::Context;
fn main() {
let data = std::io::Cursor::new(b"FFBFFFBLLL\nBFBFBBFRLR\nFFFBFFBLLL");
let seats: Result<Vec<_>, _> = data
.lines()
.map(|l| l.map(replace).context("force eyre"))
.map(|l| l.map(|s| u32::from_str_radix(&s, 2).context("force eyre")))
.map(|r| r.flatten())
.collect();
println!("{:#?}", seats);
}
fn replace(line: String) -> String {
line.replace('F', "0")
.replace('B', "1")
.replace('L', "0")
.replace('R', "1")
}
Further References:
How do I stop iteration and return an error when Iterator::map returns a Result::Err?
Result implements FromIter.
result_flatten: https://doc.rust-lang.org/std/result/enum.Result.html?search=#method.flatten, https://github.com/rust-lang/rust/issues/70142 (I'm using rustc 1.49.0-nightly (ffa2e7ae8 2020-10-24))
lines() returns Results: https://doc.rust-lang.org/std/io/trait.BufRead.html#method.lines
Since you discard the error type anyway, you can avoid eyre entirely and use .ok to convert the Result into an Option, then just work with Option's and_then to avoid flattening every time:
let seats: Option<Vec<_>> = data
.lines()
.map(|l| l.ok())
.map(|l| l.map(replace))
.map(|l| l.and_then(|s| u32::from_str_radix(&s, 2).ok()))
// if you want to keep chaining
.map(|l| l.and_then(|s| some_result_function(&s).ok()))
.collect();
If you want to just skip over the errors, a much more elegant solution exists with filter_map:
let seats: Vec<_> = data
.lines()
.filter_map(|l| l.ok())
.map(replace)
.filter_map(|l| u32::from_str_radix(&l, 2).ok())
.collect();
If you want to maintain errors, then box the errors into a Box<dyn Error> to account for different types:
use std::error::Error;
// later in the code
let seats: Result<Vec<_>, Box<dyn Error>> = data
.lines()
.map(|x| x.map_err(|e| Box::new(e) as _))
.map(|l| l.map(replace))
.map(|l| l.and_then(|s| u32::from_str_radix(&s, 2).map_err(|e| Box::new(e) as _)))
.collect();
If you don't like the repeated .map_err(|e| Box::new(e) as _), then make a trait for it:
use std::error::Error;
trait BoxErr {
type Boxed;
fn box_err(self) -> Self::Boxed;
}
impl<T, E: Error + 'static> BoxErr for Result<T, E> {
type Boxed = Result<T, Box<dyn Error>>;
fn box_err(self) -> Self::Boxed {
self.map_err(|x| Box::new(x) as Box<dyn Error>)
}
}
// later in the code
let seats: Result<Vec<_>, Box<dyn Error>> = data
.lines()
.map(|x| x.box_err())
.map(|l| l.map(replace))
.map(|l| l.and_then(|s| u32::from_str_radix(&s, 2).box_err()))
.collect();
Related
This question already has answers here:
How does the Iterator::collect function work?
(2 answers)
Closed 1 year ago.
i want to read a textfile and convert all lines into int values.
I use this code.
But what i really miss here is a "good" way of error handling.
use std::{
fs::File,
io::{prelude::*, BufReader},
path::Path
};
fn lines_from_file(filename: impl AsRef<Path>) -> Vec<i32> {
let file = File::open(filename).expect("no such file");
let buf = BufReader::new(file);
buf.lines()
.map(|l| l.expect("Could not parse line"))
.map(|l:String| l.parse::<i32>().expect("could not parse int"))
.collect()
}
Question: How to do proper error handling ?
Is this above example "good rust code" ?
or should i use something like this :
fn lines_from_file(filename: impl AsRef<Path>) -> Vec<i32> {
let file = File::open(filename).expect("no such file");
let buf = BufReader::new(file);
buf.lines()
.map(|l| l.expect("Could not parse line"))
.map(|l:String| match l.parse::<i32>() {
Ok(num) => num,
Err(e) => -1 //Do something here
}).collect()
}
You can actually collect into a Result<T, E>.
See docs
So you could collect into a Result<Vec<i32>, MyCustomErrorType>.
This works when you transform your iterator in an iterator which returns a Result<i32, MyCustomErrorType>. The iteration stops at the first Err you map.
Here's your working code example.
I used the thiserror crate for error handling
use std::{
fs::File,
io::{prelude::*, BufReader},
num::ParseIntError,
path::Path,
};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum LineParseError {
#[error("Failed to read line")]
IoError(#[from] std::io::Error),
#[error("Failed to parse int")]
FailedToParseInt(#[from] ParseIntError),
}
fn lines_from_file(filename: impl AsRef<Path>) -> Result<Vec<i32>, LineParseError> {
let file = File::open(filename).expect("no such file");
let buf = BufReader::new(file);
buf.lines().map(|l| Ok(l?.parse()?)).collect()
}
Some small explanation of how the code works by breaking down this line of code:
buf.lines().map(|l| Ok(l?.parse()?)).collect()
Rust infers that we need to collect to a Result<Vec<i32>, LineParseError> because the return type of the function is Result<Vec<i32>, LineParseError>
In the mapping method we write l? this makes the map method return an Err if the l result contains an Err, the #[from] attribute on LineParseError::IoError takes care of the conversion
The .parse()? works the same way: #[from] on LineParseError::FailedToParseInt takes care of the conversion
Last but not least our method must return Ok(...) when the mapping does succeed, this makes the collect into a Result<Vec<i32>, LineParseError> possible.
I need to put some futures in a Vec for later joining. However if I try to collect it using an iterator, the compiler doesn't seem to be able to determine the type for the vector.
I'm trying to create a command line utility that accepts an arbitrary number of IP addresses, communicates with those remotes and collects the results for printing. The communication function works well, I've cut down the program to show the failure I need to understand.
use futures::future::join_all;
use itertools::Itertools;
use std::net::SocketAddr;
use std::str::from_utf8;
use std::fmt;
#[tokio::main(flavor = "current_thread")]
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
let socket: Vec<SocketAddr> = vec![
"192.168.20.33:502".parse().unwrap(),
"192.168.20.34:502".parse().unwrap(),];
let async_vec = vec![
MyStruct::get(socket[0]),
MyStruct::get(socket[1]),];
// The above 3 lines happen to work to build a Vec because there are
// 2 sockets. But I need to build a Vec to join_all from an arbitary
// number of addresses. Why doesn't the line below work instead?
//let async_vec = socket.iter().map(|x| MyStruct::get(*x)).collect();
let rt = join_all(async_vec).await;
let results = rt.iter().map(|x| x.as_ref().unwrap().to_string()).join("\n");
let mut rvec: Vec<String> = results.split("\n").map(|x| x.to_string()).collect();
rvec.sort_by(|a, b| a[15..20].cmp(&b[15..20]));
println!("{}", rvec.join("\n"));
Ok(())
}
struct MyStruct {
serial: [u8; 12],
placeholder: String,
}
impl fmt::Display for MyStruct {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let serial = match from_utf8(&self.serial) {
Ok(v) => v,
Err(_) => "(invalid)",
};
let lines = (1..4).map(|x| format!("{}, line{}, {}", serial, x, self.placeholder)).join("\n");
write!(f, "{}", lines)
}
}
impl MyStruct {
pub async fn get(sockaddr: SocketAddr) -> Result<MyStruct, Box<dyn std::error::Error>> {
let char = sockaddr.ip().to_string().chars().last().unwrap();
let rv = MyStruct{serial: [char as u8;12], placeholder: sockaddr.to_string(), };
Ok(rv)
}
}
This line:
let async_vec = socket.iter().map(|x| MyStruct::get(*x)).collect();
doesn't work because the compiler can't know that you want to collect everything into a Vec. You might want to collect into some other container (e.g. a linked list or a set). Therefore you need to tell the compiler the kind of container you want with:
let async_vec = socket.iter().map(|x| MyStruct::get(*x)).collect::<Vec::<_>>();
or:
let async_vec: Vec::<_> = socket.iter().map(|x| MyStruct::get(*x)).collect();
I'm trying to read a file into a vector, then print out a random line from that vector.
What am I doing wrong?
I'm asking here because I know I'm making a big conceptual mistake, but I'm having trouble identifying exactly where it is.
I know the error -
error[E0308]: mismatched types
26 | processor(&lines)
| ^^^^^^ expected &str, found struct std::string::String
And I see that there's a mismatch - but I don't know how to give the right type, or refactor the code for that (very short) function.
My code is below:
use std::{
fs::File,
io::{prelude::*, BufReader},
path::Path,
};
fn lines_from_file(filename: impl AsRef<Path>) -> Vec<String> {
let file = File::open(filename).expect("no such file");
let buf = BufReader::new(file);
buf.lines()
.map(|l| l.expect("Could not parse line"))
.collect()
}
fn processor(vectr: &Vec<&str>) -> () {
let vec = vectr;
let index = (rand::random::<f32>() * vec.len() as f32).floor() as usize;
println!("{}", vectr[index]);
}
fn main() {
let lines = lines_from_file("./example.txt");
for line in lines {
println!("{:?}", line);
}
processor(&lines);
}
While you're calling the processor function you're trying to pass a Vec<String> which is what the lines_from_file returns but the processor is expecting a &Vec<&str>. You can change the processor to match that expectation:
fn processor(vectr: &Vec<String>) -> () {
let vec = vectr;
let index = (rand::random::<f32>() * vec.len() as f32).floor() as usize;
println!("{}", vectr[index]);
}
The main function:
fn main() {
let lines = lines_from_file("./example.txt");
for line in &lines {. // &lines to avoid moving the variable
println!("{:?}", line);
}
processor(&lines);
}
More generally, a String is not the same as a string slice &str, therefore Vec<String> is not the same as Vec<&str>. I'd recommend checking the rust book: https://doc.rust-lang.org/nightly/book/ch04-03-slices.html?highlight=String#string-slices
I have a function that returns a Result:
fn find(id: &Id) -> Result<Item, ItemError> {
// ...
}
Then another using it like this:
let parent_items: Vec<Item> = parent_ids.iter()
.map(|id| find(id).unwrap())
.collect();
How do I handle the case of failure inside any of the map iterations?
I know I could use flat_map and in this case the error results would be ignored:
let parent_items: Vec<Item> = parent_ids.iter()
.flat_map(|id| find(id).into_iter())
.collect();
Result's iterator has either 0 or 1 items depending on the success state, and flat_map will filter it out if it's 0.
However, I don't want to ignore errors, I want to instead make the whole code block just stop and return a new error (based on the error that came up within the map, or just forward the existing error).
How do I best handle this in Rust?
Result implements FromIterator, so you can move the Result outside and iterators will take care of the rest (including stopping iteration if an error is found).
#[derive(Debug)]
struct Item;
type Id = String;
fn find(id: &Id) -> Result<Item, String> {
Err(format!("Not found: {:?}", id))
}
fn main() {
let s = |s: &str| s.to_string();
let ids = vec![s("1"), s("2"), s("3")];
let items: Result<Vec<_>, _> = ids.iter().map(find).collect();
println!("Result: {:?}", items);
}
Playground
The accepted answer shows how to stop on error while collecting, and that's fine because that's what the OP requested. If you need processing that also works on large or infinite fallible iterators, read on.
As already noted, for can be used to emulate stop-on-error, but that is sometimes inelegant, as when you want to call max() or other method that consumes the iterator. In other situations it's next to impossible, as when the iterator is consumed by code in another crate, such as itertools or Rayon1.
Iterator consumer: try_for_each
When you control how the iterator is consumed, you can just use try_for_each to stop on first error. It accepts a closure that returns a Result, and try_for_each() will return Ok(()) if the closure returned Ok every time, and the first Err on the first error. This allows the closure to detect errors simply by using the ? operator in the natural way:
use std::{fs, io};
fn main() -> io::Result<()> {
fs::read_dir("/")?.try_for_each(|e| -> io::Result<()> {
println!("{}", e?.path().display());
Ok(())
})?;
// ...
Ok(())
}
If you need to maintain state between the invocations of the closure, you can also use try_fold. Both methods are implemented by ParallelIterator, so the same pattern works with Rayon.
try_for_each() does require that you control how the iterator is consumed. If that is done by code not under your control - for example, if you are passing the iterator to itertools::merge() or similar, you will need an adapter.
Iterator adapter: scan
The first attempt at stopping on error is to use take_while:
use std::{io, fs};
fn main() -> io::Result<()> {
fs::read_dir("/")?
.take_while(Result::is_ok)
.map(Result::unwrap)
.for_each(|e| println!("{}", e.path().display()));
// ...
Ok(())
}
This works, but we don't get any indication that an error occurred, the iteration just silently stops. Also it requires the unsightly map(Result::unwrap) which makes it seem like the program will panic on error, which is in fact not the case as we stop on error.
Both issues can be addressed by switching from take_while to scan, a more powerful combinator that not only supports stopping the iteration, but passes its callback owned items, allowing the closure to extract the error to the caller:
fn main() -> io::Result<()> {
let mut err = Ok(());
fs::read_dir("/")?
.scan(&mut err, |err, res| match res {
Ok(o) => Some(o),
Err(e) => {
**err = Err(e);
None
}
})
.for_each(|e| println!("{}", e.path().display()));
err?;
// ...
Ok(())
}
If needed in multiple places, the closure can be abstracted into a utility function:
fn until_err<T, E>(err: &mut &mut Result<(), E>, item: Result<T, E>) -> Option<T> {
match item {
Ok(item) => Some(item),
Err(e) => {
**err = Err(e);
None
}
}
}
...in which case we can invoke it as .scan(&mut err, until_err) (playground).
These examples trivially exhaust the iterator with for_each(), but one can chain it with arbitrary manipulations, including Rayon's par_bridge(). Using scan() it is even possible to collect() the items into a container and have access to the items seen before the error, which is sometimes useful and unavailable when collecting into Result<Container, Error>.
1 Needing to use par_bridge() comes up when using Rayon to process streaming data in parallel:
fn process(input: impl BufRead + Send) -> std::Result<Output, Error> {
let mut err = Ok(());
let output = lines
.input()
.scan(&mut err, until_err)
.par_bridge()
.map(|line| ... executed in parallel ... )
.reduce(|item| ... also executed in parallel ...);
err?;
...
Ok(output)
}
Again, equivalent effect cannot be trivially achieved by collecting into Result.
Handling nested .map() closure Result's
What if we have a .map() within a .map() within a .map()?
Here's an example for the specific case where the .map() operations are nested. The problem it solves is how to propagate a failure from the innermost closure while avoiding using .unwrap() which aborts the application.
This approach also enables using ? syntax at the outer layer to capture the error if one occurs, or unwrap the result to assign to a variable if no error occurred. ? can't otherwise be used from inside the closures.
.parse() as it's used below will return Result<T, ParseIntError>.
use std::error::Error;
const DATA: &str = "1 2 3 4\n5 6 7 8";
fn main() -> Result<(), Box<dyn Error>>
{
let data = DATA.lines().map(|l| l.split_whitespace()
.map(|n| n.parse() /* can fail */)
.collect())
.collect::<Result<Vec<Vec<i32>>, _>>()?;
println!("{:?}", data);
Ok(())
}
Note that the outer .collect::<..>() generic expression specifies Result<Vec<Vec<..>>. The inner .collect() will be producing Results, which are stripped away by the outer Result as it takes the Ok contents and produces the 2-D vector.
Without relying heavily on type inference, the inner .collect() generic expression would look like this:
.collect::<Result<Vec<i32>, _>>()) // <--- Inner.
.collect::<Result<Vec<Vec<i32>>, _>>()?; // <--- Outer.
Using the ? syntax, the variable, data, will be assigned this 2-D vector; or the main() function will return a parsing error that originated from within the inner closure.
output:
[[1, 2, 3, 4], [5, 6, 7, 8]]
Taking it a step further, parse results nested three levels deep can be handled this way.
type Vec3D<T, E> = Result<Vec<Vec<Vec<T>>>, E>;
const DATA: &str = "1 2 | 3 4\n5 6 | 7 8";
fn main() -> Result<(), Box<dyn Error>>
{
let data = DATA.lines()
.map(|a| a.split("|")
.map(|b| b.split_whitespace()
.map(|c| c.parse()) // <---
.collect())
.collect())
.collect::<Vec3D<i32,_>>()?;
println!("{:?}", data);
Ok(())
}
output:
[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
Or if a number couldn't be parsed, we'd get:
Error: ParseIntError { kind: InvalidDigit }
This answer pertains to a pre-1.0 version of Rust and the required functions were removed
You can use std::result::fold function for this. It stops iterating after encountering the first Err.
An example program I just wrote:
fn main() {
println!("{}", go([1, 2, 3]));
println!("{}", go([1, -2, 3]));
}
fn go(v: &[int]) -> Result<Vec<int>, String> {
std::result::fold(
v.iter().map(|&n| is_positive(n)),
vec![],
|mut v, e| {
v.push(e);
v
})
}
fn is_positive(n: int) -> Result<int, String> {
if n > 0 {
Ok(n)
} else {
Err(format!("{} is not positive!", n))
}
}
Output:
Ok([1, 2, 3])
Err(-2 is not positive!)
Demo
I have a function that returns a Result:
fn find(id: &Id) -> Result<Item, ItemError> {
// ...
}
Then another using it like this:
let parent_items: Vec<Item> = parent_ids.iter()
.map(|id| find(id).unwrap())
.collect();
How do I handle the case of failure inside any of the map iterations?
I know I could use flat_map and in this case the error results would be ignored:
let parent_items: Vec<Item> = parent_ids.iter()
.flat_map(|id| find(id).into_iter())
.collect();
Result's iterator has either 0 or 1 items depending on the success state, and flat_map will filter it out if it's 0.
However, I don't want to ignore errors, I want to instead make the whole code block just stop and return a new error (based on the error that came up within the map, or just forward the existing error).
How do I best handle this in Rust?
Result implements FromIterator, so you can move the Result outside and iterators will take care of the rest (including stopping iteration if an error is found).
#[derive(Debug)]
struct Item;
type Id = String;
fn find(id: &Id) -> Result<Item, String> {
Err(format!("Not found: {:?}", id))
}
fn main() {
let s = |s: &str| s.to_string();
let ids = vec![s("1"), s("2"), s("3")];
let items: Result<Vec<_>, _> = ids.iter().map(find).collect();
println!("Result: {:?}", items);
}
Playground
The accepted answer shows how to stop on error while collecting, and that's fine because that's what the OP requested. If you need processing that also works on large or infinite fallible iterators, read on.
As already noted, for can be used to emulate stop-on-error, but that is sometimes inelegant, as when you want to call max() or other method that consumes the iterator. In other situations it's next to impossible, as when the iterator is consumed by code in another crate, such as itertools or Rayon1.
Iterator consumer: try_for_each
When you control how the iterator is consumed, you can just use try_for_each to stop on first error. It accepts a closure that returns a Result, and try_for_each() will return Ok(()) if the closure returned Ok every time, and the first Err on the first error. This allows the closure to detect errors simply by using the ? operator in the natural way:
use std::{fs, io};
fn main() -> io::Result<()> {
fs::read_dir("/")?.try_for_each(|e| -> io::Result<()> {
println!("{}", e?.path().display());
Ok(())
})?;
// ...
Ok(())
}
If you need to maintain state between the invocations of the closure, you can also use try_fold. Both methods are implemented by ParallelIterator, so the same pattern works with Rayon.
try_for_each() does require that you control how the iterator is consumed. If that is done by code not under your control - for example, if you are passing the iterator to itertools::merge() or similar, you will need an adapter.
Iterator adapter: scan
The first attempt at stopping on error is to use take_while:
use std::{io, fs};
fn main() -> io::Result<()> {
fs::read_dir("/")?
.take_while(Result::is_ok)
.map(Result::unwrap)
.for_each(|e| println!("{}", e.path().display()));
// ...
Ok(())
}
This works, but we don't get any indication that an error occurred, the iteration just silently stops. Also it requires the unsightly map(Result::unwrap) which makes it seem like the program will panic on error, which is in fact not the case as we stop on error.
Both issues can be addressed by switching from take_while to scan, a more powerful combinator that not only supports stopping the iteration, but passes its callback owned items, allowing the closure to extract the error to the caller:
fn main() -> io::Result<()> {
let mut err = Ok(());
fs::read_dir("/")?
.scan(&mut err, |err, res| match res {
Ok(o) => Some(o),
Err(e) => {
**err = Err(e);
None
}
})
.for_each(|e| println!("{}", e.path().display()));
err?;
// ...
Ok(())
}
If needed in multiple places, the closure can be abstracted into a utility function:
fn until_err<T, E>(err: &mut &mut Result<(), E>, item: Result<T, E>) -> Option<T> {
match item {
Ok(item) => Some(item),
Err(e) => {
**err = Err(e);
None
}
}
}
...in which case we can invoke it as .scan(&mut err, until_err) (playground).
These examples trivially exhaust the iterator with for_each(), but one can chain it with arbitrary manipulations, including Rayon's par_bridge(). Using scan() it is even possible to collect() the items into a container and have access to the items seen before the error, which is sometimes useful and unavailable when collecting into Result<Container, Error>.
1 Needing to use par_bridge() comes up when using Rayon to process streaming data in parallel:
fn process(input: impl BufRead + Send) -> std::Result<Output, Error> {
let mut err = Ok(());
let output = lines
.input()
.scan(&mut err, until_err)
.par_bridge()
.map(|line| ... executed in parallel ... )
.reduce(|item| ... also executed in parallel ...);
err?;
...
Ok(output)
}
Again, equivalent effect cannot be trivially achieved by collecting into Result.
Handling nested .map() closure Result's
What if we have a .map() within a .map() within a .map()?
Here's an example for the specific case where the .map() operations are nested. The problem it solves is how to propagate a failure from the innermost closure while avoiding using .unwrap() which aborts the application.
This approach also enables using ? syntax at the outer layer to capture the error if one occurs, or unwrap the result to assign to a variable if no error occurred. ? can't otherwise be used from inside the closures.
.parse() as it's used below will return Result<T, ParseIntError>.
use std::error::Error;
const DATA: &str = "1 2 3 4\n5 6 7 8";
fn main() -> Result<(), Box<dyn Error>>
{
let data = DATA.lines().map(|l| l.split_whitespace()
.map(|n| n.parse() /* can fail */)
.collect())
.collect::<Result<Vec<Vec<i32>>, _>>()?;
println!("{:?}", data);
Ok(())
}
Note that the outer .collect::<..>() generic expression specifies Result<Vec<Vec<..>>. The inner .collect() will be producing Results, which are stripped away by the outer Result as it takes the Ok contents and produces the 2-D vector.
Without relying heavily on type inference, the inner .collect() generic expression would look like this:
.collect::<Result<Vec<i32>, _>>()) // <--- Inner.
.collect::<Result<Vec<Vec<i32>>, _>>()?; // <--- Outer.
Using the ? syntax, the variable, data, will be assigned this 2-D vector; or the main() function will return a parsing error that originated from within the inner closure.
output:
[[1, 2, 3, 4], [5, 6, 7, 8]]
Taking it a step further, parse results nested three levels deep can be handled this way.
type Vec3D<T, E> = Result<Vec<Vec<Vec<T>>>, E>;
const DATA: &str = "1 2 | 3 4\n5 6 | 7 8";
fn main() -> Result<(), Box<dyn Error>>
{
let data = DATA.lines()
.map(|a| a.split("|")
.map(|b| b.split_whitespace()
.map(|c| c.parse()) // <---
.collect())
.collect())
.collect::<Vec3D<i32,_>>()?;
println!("{:?}", data);
Ok(())
}
output:
[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
Or if a number couldn't be parsed, we'd get:
Error: ParseIntError { kind: InvalidDigit }
This answer pertains to a pre-1.0 version of Rust and the required functions were removed
You can use std::result::fold function for this. It stops iterating after encountering the first Err.
An example program I just wrote:
fn main() {
println!("{}", go([1, 2, 3]));
println!("{}", go([1, -2, 3]));
}
fn go(v: &[int]) -> Result<Vec<int>, String> {
std::result::fold(
v.iter().map(|&n| is_positive(n)),
vec![],
|mut v, e| {
v.push(e);
v
})
}
fn is_positive(n: int) -> Result<int, String> {
if n > 0 {
Ok(n)
} else {
Err(format!("{} is not positive!", n))
}
}
Output:
Ok([1, 2, 3])
Err(-2 is not positive!)
Demo