Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Option types and early returns. return an Error when is_none()

Tags:

rust

Using match (like in bar) seems to be a common approach..

#[derive(Debug)]
pub enum MyErrors {
    SomeError,
}

fn foo(x: Option<u64>) -> Result<u64, MyErrors> {
    if x.is_none() {
      return Err(MyErrors::SomeError);
    } 

    // .. some long code where more options
    // are checked and matched 
    // The ok here is just so the code is simple and compiles
    Ok(x.unwrap() * 2)
}

fn bar(x: Option<u64>) -> Result<u64, MyErrors> {
    match x {
        None => {
            return Err(MyErrors::SomeError)?;
        }
        Some(v) => {
           // .. some long code where more options
           // are checked and matched 
           // The ok here is just so the code is simple and compiles
           Ok(x.unwrap() * 2)
        }
    }
}


fn main() {
    foo(Some(1));
    bar(Some(2));
}

However, early returns (such as in foo) significantly reduce how nested the code looks like. If there are multiple times when an option has to be unwrapped or an error returned, code like bar gets very nested...

What is the recommended practice for early returning an error in the case of empty options?

like image 916
Juan Leni Avatar asked Mar 22 '19 21:03

Juan Leni


People also ask

What is Option <> in Rust?

Option types are very common in Rust code, as they have a number of uses: Initial values. Return values for functions that are not defined over their entire input range (partial functions) Return value for otherwise reporting simple errors, where None is returned on error. Optional struct fields.

How do you check if a option is none in Rust?

To check if an Option is None you can either use Option::is_none or use the if let syntax. or if x == None .

What is some and none in Rust?

The Option<T> enum in Rust can cater to two variants: None : represents a lack of value or if an error is encountered. Some(value) : value with type T wrapped in a tuple.


1 Answers

If a longer method chain is undesirable due to complex logic inside, there are still a few readable, low-indent options.

ok_or and ?

We can convert an Option to a Result with a desired error, and immediately unwrap it with the ? operator. This solution probably provides the least indent possible, and can be easily used to "unwrap" multiple Options.

fn bar1(x: Option<u64>) -> Result<u64, MyErrors> {
    let x = x.ok_or(MyErrors::SomeError)?;
    // A lot of stuff going on.
    Ok(x * 2)
}

This will evaluate the error inside ok_or regardless of whether or not it will actually be used. If this computation is expensive, ok_or_else, which produces the error lazily, will be more efficient (related question).

if let

This solution can still lead to a staircase of code if nested, but may be more appropriate if the else branch logic is more involved.

fn bar2(x: Option<u64>) -> Result<u64, MyErrors> {
    if let Some(x) = x {
        // Lot of stuff here as well.
        Ok(x * 2)
    } else {
        Err(MyErrors::SomeError)
    }
}
like image 178
Michail Avatar answered Oct 12 '22 01:10

Michail