I'm kinda new to Rust and there are lots of match patterns, which I find great. The problem is that it tends to get nested if I want to do lots of things that need match patterning:
let codec = ffmpeg_next::codec::find_by_name("h264");
match codec {
Ok(codec) => {
//... more nested matches
},
Err(e) => {
}
}
I could simply unwrap() like this:
let codec = ffmpeg_next::codec::find_by_name("h264");
coded.unwrap().do_something();
however I don want panics, I want to handle errors if something happens.
let codec = ffmpeg_next::codec::find_by_name("h264");
if !codec.contains() {
//deal with error and return
}
I could use match for it:
let codec = ffmpeg_next::codec::find_by_name("h264");
match codec {
Ok(codec) => {
// do nothing
},
Err(e) => {
//deal with error and return
}
}
//continue using codec here
but I think I'm cheating Rust's error handling patterns.
Just elaborating on this answer
? on a Result<T, E> will convert Ok(T) to a T and will immediately return an Err(E). For this to work the return type of the function must also be a Result<T2, E2> and E must implement Into<E2>
if you don't wanna deal with all this Into implementations check out anyhow
that being said I think it's find to nest them if you want to.
There are two approaches for this. For small one-liners, there are helper methods like and_then, map, or_else, which are a different variations of callbacks that operate on "wrapped" values.
Check docs of std Option and Result. There are lots of useful helpers there.
Second option is to use the ? operator in functions that return Result or Option.
find_by_name(n)?.do_something()?;
There's .ok_or(SomeError) to convert Option to Result, so that you can use it with ? in Result-returning functions. There's also .ok() to do the other way.
For something like this where you only want one side of the match, an if let is easier to read, since it states right up front which case you're looking at:
codec::find_by_name("h264");
if let Err(e) = codec {
//deal with error and return
}
This makes these matches significantly less painful. But also listen especially to @kornel's advice about helper methods, and the possibility others mention to use ? to delay error handling to the caller.
let video_decoder = self.context.take().unwrap().decoder().open_as(codec);
//I could forget to verify for error here
if let Err(e) = video_decoder {
//deal with error
return;
}
//so I can unwrap safely here
let video_decoder = video_decoder.unwrap();
when I say I'm kinda cheating Rust's error handling system, I mean that I could easily forget to verify the error before unwraping. I can unwrap safely at the last line just because I verified it on the lines before.
The good thing about match is that it forces me to verify things:
let video_decoder = self.context.take().unwrap().decoder().open_as(codec);
//there's no way I forget to verify an error here
match video_decoder {
Ok() => {}
Err(e) => {}
}
but the only reason I'm trying to avoid it its because of too much nested matches.
I want to take out my responsibility of remembering to check things before unwraping. I want Rust to remember me to do things, like it does with match.