Can I use '<' and '>' in match?
Asked Answered
L

4

49

I am trying to do a simple quadratic function that would return number of roots and their values via an enum:

enum QuadraticResult {
    None,
    OneRoot(f32),
    TwoRoots(f32, f32),
}

fn solveQuadratic(a: f32, b: f32, c: f32) -> QuadraticResult {
    let delta = b * b - 4.0 * a * c;
    match delta {
        < 0 => return QuadraticResult::None,
        > 0 => return QuadraticResult::TwoRoots(0.0, 1.0),
        _ => return QuadraticResult::OneRoot(0.0),
    }
}

This doesn't compile as it complains about '<' and '>'. Is there a way to achieve this with match or do I need to use if

Limousine answered 17/12, 2017 at 4:47 Comment(3)
Idiomatic Rust uses snake_case for identifiers. solve_quadratic not solveQuadratic.Hydria
@Hydria Good point. Luckily rls plugin for Visual Studio Code flagged this for me as well after I was able to compile it. :)Limousine
I suppose you're eventually going to replace 0.0 and 1.0 with calculated values. Be careful not to assume that, when QuadraticResult::TwoRoots(x, y) is returned, x != y. Even if d is greater than 0, -b + d.sqrt() and -b - d.sqrt() may still be the same. Here's an easy example, but it could happen with finite roots too. You may want to return Option<(x1, x2)> and let the caller decide whether x1 and x2 are "close enough" to be considered a single root.Anubis
S
76

You can use a match guard, but that feels more verbose than a plain if statement:

return match delta {
    d if d < 0 => QuadraticResult::None,
    d if d > 0 => QuadraticResult::TwoRoots(0.0, 1.0),
    _   => QuadraticResult::OneRoot(0.0),
}
Stane answered 17/12, 2017 at 4:57 Comment(3)
Just wanted to add that I definitely prefer 'match' than 'if/else' statements in this case as the code looks much neater and readable IMHO.Limousine
Another thing came to mind. Is there any obvious performance difference between 'if/else' and 'match'?Limousine
@Limousine even if you will have any difference in performance - it will be not guarantee to stay the same on next version. You shouldn't bother about performance at this level if you aren't force to do. First is readability - optimization is compiler work. Here you can check assemble code of match and ifHjerpe
H
30

If you want to handle the three cases where some value is greater than, equal to or less than another, you can match on an Ordering, which you can obtain by calling cmp (from the Ord trait) or partial_cmp (from the PartialOrd trait).

fn solve_quadratic(a: f32, b: f32, c: f32) -> QuadraticResult {
    let delta = b * b - 4.0 * a * c;
    match delta.partial_cmp(&0.0).expect("I don't like NaNs") {
        Ordering::Less => QuadraticResult::None,
        Ordering::Greater => QuadraticResult::TwoRoots(0.0, 1.0),
        Ordering::Equal => QuadraticResult::OneRoot(0.0),
    }
}
Horrorstruck answered 17/12, 2017 at 13:27 Comment(0)
A
11

You can, but you'll want to create a variable binding when you do it and turn it into an actual expression:

match delta {
    d if d < 0.0 => QuadraticResult::None,
    d if d > 0.0 => QuadraticResult::TwoRoots(0.0, 1.0),
    _ => QuadraticResult::OneRoot(0.0),
}

I'm not sure this is any better than just splitting this into an if statement though.

Allaallah answered 17/12, 2017 at 4:59 Comment(2)
Thank you Simon. I will have to accept Jacob's answer as he was first :)Limousine
Oh no problem at all. I didn't see Jacobs answer ... hah, they're nearly identical too! I may as well remove mine.Allaallah
A
1

Warning: as of rustc 1.67.0 (fc594f156 2023-01-24) this method still works, but will stop working for float literals in a future release.

You can use a Range Pattern which are supported in match expressions. This requires the exclusive_range_pattern feature.

#![feature(exclusive_range_pattern)]
fn solveQuadratic(a: f32, b: f32, c: f32) -> QuadraticResult {
    let delta = b * b - 4.0 * a * c;
    match delta {
        std::f32::MIN..0.0 => return QuadraticResult::None,
        0.0 => return QuadraticResult::OneRoot(0.0),
        _ => return QuadraticResult::TwoRoots(0.0, 1.0),
    }
}

Playground

Aegir answered 9/2, 2023 at 14:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.