How to unwrap or deal with None without nesting matches

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.

How can I unwrap OR deal with the error.

I found https://doc.rust-lang.org/beta/std/option/enum.Option.html#method.contains but its experimental:

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.

How so? A match lets you explicitly handle both the success and failure cases.

If you just want to return early with an Err when a call fails, you can use the ? operator.

1 Like

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.

find_by_name(n).map(|inner| inner.do_something());

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.

Take this example:

    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.

Sounds like you probably want

let codec = match codec {
        Ok(codec) => codec,
        Err(e) => {
            //deal with error and return 
        }
    };

No unwrapping and no nesting.

Nice. I forgot I don't need to return something with the type of codec if I return inside Err(e)=>{}

you might also like .unwrap_or(val) and its close cousins, .unwrap_or_else(|| get_val()) and .unwrap_or_default().

1 Like