Guidance on Result && Option

One area where I get a bit choked up on in Rust is dealing with the Result<T,E> and Option<T> types.

For example, I will use a function which returns one of those types and then store the return in a var, which ends up moving to another function or var, and eventually, I need to do something like print it out or copy it into a struct and now I’m stuck with a Result or Option and at that point, I have to do a match and sometimes I feel like it can get a bit sloppy. Alternatively, if I store a Option<T> inside of the struct, now, whenever I need to use the struct member, I have to then do a match, etc…

The only alternative I know of is to do the match right away as soon as the first function returns and then store the hard value in the case of Ok(v) or Some(v). However, now I am left with making the remainder of the code stem from the match arm because I cannot execute on a value which we don’t know actually exists…

I tend to try and use match statements a lot, should I just be using .expect() and/or ? more often? For some reason, I thought those were sorta bad practice. This wasn’t as complicated in C because I would just do NULL and check for NULL, or #define some STATUSes etc… At the expense of far less safety of course.

Tl;dr - I have a difficult time telling at which stage in my program I should call match on my Result and Option values.

The ? operator just propogates Result/Option and desugars to a match. Use unwrap/except if you know that ut will be Ok/Some or at the top level application (not in deep internals or libraries). Otherwise you have to use a match or if let.

1 Like

Option and Result are used to indicate a possible absence of a value. If your function may or may not return a value, use Option or Result. If your struct may or may not contain a value, use Option or Result. Same goes for local variables, function arguments, and so on. First, you should think about where in your program you have optionally present values. Choose field types and function signatures based on their semantics. Next, when you implement functions, Rust will force you to use match/unwrap/? when you have to convert an optionally present value to an always present one, but that would be necessary anyway. This way, you will avoid useless match statements where you don't really have multiple cases.

expect is not a good way to handle errors, since it will just panic if the value is not present. If you don't need to handle the errors, it's acceptable to use expect. ? is completely fine, and it will make the code cleaner, compared to doing match manually all the time.

If you do an early return in the bad case, you can write the rest of the function with the unwrapped value:

let data = match func() {
    Ok(data) => data,
    Err(err) => {
        return Err(err);
    }
}
// you can access `data` directly here

The ? operator does almost the same thing (in addition, it will convert the error type if needed):

let data = func()?;
// you can access `data` directly here
1 Like

?, match and if let are all best practice and you have a lot of useful methods on Option/Result like .map(), .and_then(), .unwrap_or()

3 Likes