What is the cleanest way to return the error when matching a Result<T, E>? [duplicate] - error-handling

This question already has answers here:
Is there any way of doing unwrap_or_return an Error (any error)
(1 answer)
What is this question mark operator about?
(4 answers)
Closed 2 years ago.
I'm calling a function that returns a Result<T, E> and I want to handle the Ok case but return the Err as is if an error is returned. What is the cleanest way to do this?
For example, I have:
fn example() -> Result<(), Error> {
match foo() {
Ok(v) => bar(v),
Err(e) => Err(e),
}
}
What are some alternate forms of writing this? It feels weird to re-wrap e in another Err in every call in the stack. Also, every call is basically 4 lines of boilerplate... I'm just looking for a way to simplify and make it more readable. My desire is to have it return the error if error, else process the result value.

Rust includes an 'error propagation' operator, ?. If the value of the Result it is called on is Ok it will unwrap and return the inner value. If the value is Err it will return from the function and pass the Err to the caller.
fn example() -> Result<(), Error> {
let v = foo()?;
bar(v);
}

Related

Conditionally Acting on Specific Error Using and_then in Rust

How do I know what error happened when piping Results via and_then in Rust? For example using nested matches I could do this:
use std::num::ParseIntError;
fn get_num(n: u8) -> Result<u8, ParseIntError> {
// Force a ParseIntError
"foo ".parse()
}
fn add_one(n: u8) -> Result<u8, ParseIntError> {
Ok(n + 1)
}
fn main() {
match get_num(1) {
Ok(n) => {
match add_one(n) {
Ok(n) => println!("adding one gives us: {}", n),
Err(why) => println!("Error adding: {}", why)
}
},
Err(why) => println!("Error getting number: {}", why),
}
}
Or I can use and_then to pipe the result of the first function to the second and avoid a nested match:
match get_num(1).and_then(add_one) {
Ok(n) => println!("Adding one gives us: {}", n),
Err(why) => println!("Some error: {}.", why)
}
How do I conditionally and idiomatically determine the error in the second form, using and_then? Do I have to type check the error? Sure I can display the error like I did above, but let's say I want to preface it with something related to the kind of error it is?
The normal way would be to use map_err to convert the initial errors to something richer you control e.g.
enum MyError {
Parsing,
Adding
}
get_num(1).map_err(|_| MyError::Parsing)
.and_then(|v| add_one(v).map_err(|_| MyError::Adding)
sadly as you can see this requires using a lambda for the and_then callback: and_then must return the same error type as the input, so here it has to yield a Result<_, MyError>, wjhich is not the return type of add_one.
And while in other situations we could recover somewhat with ?, here the same original error type is split into two different values.

Cannot move io::Error out of Peekable Result

I'm just trying to propagate the IO error:
enum MyError {
EOF,
IO(std::io::Error),
}
fn peek_byte<R>(mut p: Peekable<Bytes<R>>) -> Result<u8, MyError>
where
R: Read,
{
match p.peek() {
None => Err(MyError::EOF),
Some(Err(e)) => Err(MyError::IO(*e)), // <==== error is here
Some(Ok(byte)) => Ok(*byte),
}
}
But, I get the following error:
error[E0507]: cannot move out of `*e` which is behind a shared reference
--> src/main.rs:17:41
|
17 | Some(Err(e)) => Err(MyError::IO(*e)),
| ^^ move occurs because `*e` has type `std::io::Error`, which does not implement the `Copy` trait
I actually understand all of this. I know why I'm getting the error, and what the error means. What I don't know is how to accomplish my task and propagate the IO error into my error type.
I have tried e, *e, e.clone(), *e.clone(), *(e.clone()), but they all either produce a "type mismatch" or a "cannot move" error.
The Peekable iterator holds ownership of the next value of its internal iterator and returns references via peek, but if you actually want the owned value you just call next as usual (which does advance the iterator but I think that's okay in this case since you're not actually consuming any content from the iterator but just trying to return an error):
use std::io;
use std::io::Bytes;
use std::io::Read;
use std::iter::Peekable;
enum MyError {
EOF,
IO(io::Error),
}
fn peek_byte<R>(mut p: Peekable<Bytes<R>>) -> Result<u8, MyError>
where
R: Read,
{
match p.peek() {
None => Err(MyError::EOF),
Some(Err(e)) => Err(MyError::IO(p.next().unwrap().unwrap_err())),
Some(Ok(byte)) => Ok(*byte),
}
}
playground

Distinguish functions with lambda argument by lambda's return type?

I have a function timeout(...) (extension function that returns this) which accepts an argument that is either String, Date or Long. What I am trying to do is to make it accept any lambda that also returns one of these three types.
Kotlin finds the below functions ambiguous and can't decide which one to call when I type, for example, timeout { "something" }.
#JvmName("timeoutString")
fun <CR: CachableResponse> CR.timeout(timeLambda: CR.()->String): CR = timeout(timeLambda())
#JvmName("timeoutLong")
fun <CR: CachableResponse> CR.timeout(timeLambda: CR.()->Long): CR = timeout(timeLambda())
#JvmName("timeoutDate")
fun <CR: CachableResponse> CR.timeout(timeLambda: CR.()->Date): CR = timeout(timeLambda())
The error I'm getting is Cannot choose among the following candidates without completing type inference.
Of course one way to work around this, is to have one function instead of three like this:
fun <CR: CachableResponse, Type> CR.timeout(timeLambda: CR.()->Type): CR =
timeLambda().let { when (it) {
is String -> timeout(it)
is Date -> timeout(it)
is Long -> timeout(it)
else -> this
} }
In this case, though, the developer won't have any clue what its lambda will have to return without reading the description or checking the source code.
Is there any more elegant solution?
Actually, you solution is rather elegant.
I would only suggest to inline CR generic parameter and capture when subject in a variable:
fun <Type> CachableResponse.timeout(timeLambda: CachableResponse.() -> Type) =
when (val it = timeLambda()) {
is String -> timeout(it)
is Date -> timeout(it)
is Long -> timeout(it)
else -> this
}
In this case, though, the developer won't have any clue what its lambda will have to return without reading the description or checking the source code.
IDE comes to the rescue:

Why does the ? operator report the error "the trait bound NoneError: Error is not satisfied"?

The ? operator at line 9 works OK, but if I use the same logic on the same type in line 19, it blows up.
use std::error::Error;
use walkdir::WalkDir;
fn main() -> Result<(), Box<dyn Error>> {
let valid_entries = WalkDir::new("/tmp")
.into_iter()
.flat_map(|e| e)
.flat_map(|e| {
let name = e.file_name().to_str()?; // <-- this works
if name.contains(".txt") {
Some(e)
} else {
None
}
});
for entry in valid_entries {
println!("This file matches: {:?}", entry);
let name_to_str = entry.file_name().to_str()?; // <-- this blows up
// ...
}
Ok(())
}
The errors are a little cryptic for me to interpret:
error[E0277]: the trait bound `std::option::NoneError: std::error::Error` is not satisfied
--> src/main.rs:19:53
|
26 | let name_to_str = entry.file_name().to_str()?;
| ^ the trait `std::error::Error` is not implemented for `std::option::NoneError`
|
= note: required because of the requirements on the impl of `std::convert::From<std::option::NoneError>` for `std::boxed::Box<dyn std::error::Error>`
= note: required by `std::convert::From::from`
Why is the ? operator blowing up while iterating valid_entries?
The ? can be used to check-and-return any type that implements the Try trait (still unstable). The only implementations in std of those are Option<T> and Result<T, E> (plus some Future-related impls that are not relevant to this discussion). This means that you can use the ? operator in any function that returns Result<T, E> or Option<T>.
But you cannot mix-n-match those. That is, if your function returns a Result<T, E> you cannot use the ? in a value of type Option<T>. Or vice versa.
The reason your first ? works is because you are inside a flat_map() that returns Option<String> and all goes well. The second one, however, is in a function that returns a Result<(), Box<dyn Error>> so you can't use ? with an Option<String>.
The solution is simply to deal with the None in your Option<String> in another way:
Doing a match / if let Some(x) to handle the error separately.
Converting into a Result<String, Error> and use ?, for example with .ok_or(std::io::ErrorKind::InvalidData)?;.
Similar to 2, but take advantage of the impl From<&str> for Box<dyn Error> and do .ok_or("invalid file name").
Giving a default value, with Option::unwrap_or() or similar.
Doing unwrap() and panicking if None.
Ok, but what does this error mean? The idea is that actually you are able to use ? with an Option<T> that returns a Result<T, E>, as long as your E implements From<std::option::NoneError>. Unfortunately, NoneError is still unstable, too, so you can't implement From<NoneError> in your code using the stable compiler. Nevertheless, the E in your code is Box<dyn Error>, and the compiler would be happy to do the boxing as long as NoneError implements Error, but...
error[E0277]: the trait bound `std::option::NoneError: std::error::Error` is not satisfied

From Option to iterator [duplicate]

This question already has answers here:
How to convert an Option<T> to an iterator of zero or one element?
(1 answer)
How can I iterate on an Option<Vec<_>>?
(4 answers)
Closed 4 years ago.
I have a function which consumes an iterator:
fn func<T: iter::Iterator<Item = char>>(i: T) {
//...
}
I need to write a function whose input is an Option<char> (type char is not important here, just for illustrative purpose), and depends on the value of input, it should create an empty or an once iterator. My solution is:
use std::iter;
fn option_iter(input: Option<char>) {
let i: Box<iter::Iterator<Item = char>> = if let Some(input) = input {
Box::new(iter::once(input))
} else {
Box::new(iter::empty())
};
}
I find this ugly because of type erasure by Box. I cannot use:
let i = if let Some(input) = input {
iter::once(input)
} else {
iter::empty()
};
func(i);
because the compiler complains the types of two branches are different. Is there any method which does not use Box for this situation?
Option has an iter method that does what you want.