22

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

Shepmaster
  • 274,917
  • 47
  • 731
  • 969
dawid
  • 415
  • 2
  • 4
  • 12
  • Idiomatic Rust uses `snake_case` for identifiers. `solve_quadratic` not `solveQuadratic`. – Shepmaster Dec 17 '17 at 05:36
  • @Shepmaster Good point. Luckily rls plugin for Visual Studio Code flagged this for me as well after I was able to compile it. :) – dawid Dec 17 '17 at 10:33
  • 2
    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](https://play.rust-lang.org/?gist=f739a13470238a96374b0b8826b82a88&version=stable), but it could happen with finite roots too. You may want to return `Option` and let the caller decide whether `x1` and `x2` are "close enough" to be considered a single root. – trentcl Dec 17 '17 at 14:09

3 Answers3

32

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),
}
Jacob Krall
  • 25,736
  • 6
  • 59
  • 74
  • 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. – dawid Dec 17 '17 at 05:08
  • Another thing came to mind. Is there any obvious performance difference between 'if/else' and 'match'? – dawid Dec 17 '17 at 11:21
  • @dawid 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 if](https://rust.godbolt.org/z/ZBZzRx) – S.R Jan 24 '20 at 22:05
19

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),
    }
}
Francis Gagné
  • 46,633
  • 3
  • 125
  • 120
6

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.

Simon Whitehead
  • 57,414
  • 7
  • 93
  • 127