How do I check if both variables are both Some?

Viewed 2382

I am confused about the Some(T) keyword.

I want to check for two variables, if the value is defined (not None). If that is the case, the value of this variables is processed.

I know the match pattern which works like this:

match value {
    Some(val) => println!("{}", val),
    None => return false,
}

If I use this pattern, it will get very messy:

match param {
    Some(par) => {
        match value {
            Some(val) => {
                //process
            },

            None => return false,
        }
    },

    None => return false,
}

This can't be the right solution.

The is a possibility, to ask if the param and value is_some() That would effect code like that:

if param.is_some() && value.is_some() {
    //process
}

But if I do it like that, I always have to unwrap param and value to access the values.

I thought about something like this to avoid that. But this code does not work:

if param == Some(par) && value == Some(val) {
    //process
}

The idea is that the values are accessible by par and val like they are in the match version.

Is there any solution to do something like this?

3 Answers

If I have several Option values to match, I match on a tuple of the values:

enum Color {
    Red,
    Blue,
    Green,
}

fn foo(a: Option<Color>, b: Option<i32>) {
    match (a, b) {
        (Some(Color::Blue), Some(n)) if n > 10 => println!("Blue large number"),
        (Some(Color::Red), _) => println!("Red number"),
        _ => (),
    }
}

fn main() {
    foo(Some(Color::Blue), None);
    foo(Some(Color::Blue), Some(20));
}         

This allows me to match the combinations that are interesting, and discard the rest (or return false, if that is what you want to do).

If your function is processing multiple Option values, and would like to discard them if they're not Some, your function could return an Option itself:

fn foo(param: Option<usize>, value: Option<usize>) -> Option<usize> {
    let result = param? + value?;
    Some(result)
}

This will short-circuit the function in case there's a None value stored in either param or value.

Please read the book for more information on the ? operator.

If your function can't return an Option, you can still get away with destructuring using if let or match:

let x = if let (Some(p), Some(v)) = (param, value) {
    p + v
} else {
    return 0;
}
let x = match (param, value) {
    (Some(p), Some(v)) => p + v,
    (Some(p), _) => p,
    (_, Some(v) => v,
    _ => return 0,
}

There's a couple more alternatives not yet listed:

If you're willing to use experimental features (and hence the nightly compiler) you can use a try block as an alternative of extracting a function.

#![feature(try_blocks)]

fn main() {
    let par: Option<f32> = Some(1.0f32);
    let value: Option<f32> = Some(2.0f32);

    let x: Option<f32> = try { par? + value? };

    println!("{:?}", x);
}

Another alternative is to use map which only applies if the value is not None

let x: Option<f32> = par.map(|p| value.map(|v| p + v));
Related