Comment by esafak

Comment by esafak a day ago

5 replies

I agree, except for this example, where the author effectively (after a substitution) prefers the former:

    fn f() -> E {
      if condition {
        E::Foo(x)
      } else {
        E::Bar(y)
      }
    }
    
    fn g(e: E) {
      match e {
        E::Foo(x) => foo(x),
        E::Bar(y) => bar(y)
      }
    }
The latter is not only more readable, but it is safer, because a match statement can ensure all possibilities are covered.
CraigJPerry a day ago

> Prefers the former after a substitution...

That's not quite right, it's a substitution AND ablation of 2 functions and an enum from the code base.

There's quite a reduction in complexity he's advocating for.

Further, the enum and the additional boilerplate is not adding type safety in this example. Presumably the parameters to foo and bar are enforced in all cases so the only difference between the two examples is the additional boilerplate of a 2-armed enum.

I strongly suspect in this case (but i haven't godbolted it to be sure) that both examples compile to the same machine code. If my hunch is correct, then the remaining question is, does introduction of double-entry book keeping on the if condition add safety for future changes?

Maybe. But at what cost? This is one of those scenarios where you bank the easy win of reduced complexity.

josephg a day ago

> The latter is not only more readable, but it is safer, because a match statement can ensure all possibilities are covered.

Whether or not this matters depends on what, exactly, is in those match arms. Sometimes there's some symmetry to the arms of an if statement. And in that case, being exhaustive is important. But there's plenty of times where I really just have a bit of bookkeeping to do, or an early return or something. And I only want to do it in certain cases. Eg if condition { break; } else { stuff(); }

Also, if-else is exhaustive already. Its still exhaustive even if you add more "else if" clauses, like if {} else if {} else {}.

Match makes sense when the arms of the conditional are more symmetrical. Or when you're dealing with an enum. Or when you want to avoid repeating conditions. (Eg match a.cmp(b) { Greater / Equal / Less } ).

The best way to structure your code in general really comes down to what you're trying to do. Sometimes if statements are cleaner. Sometimes match expressions. It just depends on the situation.

  • esafak a day ago

    It's only exhaustive in this toy case. Add another one and the burden of checking for exhaustiveness with ifs falls on your shoulders.

    • josephg a day ago

      So long as you have an else block on your if statement, it’s exhaustive. I think I can keep track of that.

      • esafak a day ago

        Just because your code flowed into the else block, it does not mean the condition got handled properly. If different switching values don't need special treatment, why have an if statement at all? Consider serving an ML model, and switching on the provider. Let's say you initially support OpenAI, and self-hosting, as the if and else cases, respectively. If you then add support for Anthropic, it will incorrectly follow the else path and be treated as self-hosted. Or you make else the error path, and fail when you should not have.