Remix.run Logo
don-code a day ago

> All if, else if constructs will contain either a final else clause or a comment indicating why a final else clause is not necessary.

I actually do this as well, but in addition I log out a message like, "value was neither found nor not found. This should never happen."

This is incredibly useful for debugging. When code is running at scale, nonzero probability events happen all the time, and being able to immediately understand what happened - even if I don't understand why - has been very valuable to me.

kace91 a day ago | parent | next [-]

I like rust matching for this reason: You need to cover all branches.

In fact, not using a default (the else clause equivalent) is ideal if you can explicitly cover all cases, because then if the possibilities expand (say a new value in an enum) you’ll be annoyed by the compiler to cover the new case, which might otherwise slip by.

uecker 15 hours ago | parent [-]

And I like using enums in C ;-) The compiler tells you to cover all branches.

https://godbolt.org/z/bY1P9Kx7n

kace91 11 hours ago | parent | next [-]

Rust is a bit smarter than that, in that it covers exhaustiveness of possible states, for more than just enums:

fn g(x: u8) { match x { 0..=10 => {}, 20..=200 => {},

    }
}

That for example would complain about the ranges 11 to 19 and 201 to 255 not being covered.

You could try to map ranges to enum values, but then nobody would guarantee that you covered the whole range while mapping to enums so you’d be moving the problem to a different location.

Rust approach is not flawless, larger data types like i32 or floats can’t check full coverage (I suppose for performance reasons) but still quite useful.

uecker 11 hours ago | parent [-]

In principle C compilers can do this too https://godbolt.org/z/Ev4berx8d although you need to trick them to do this for you. This could certainly be improved.

rundev 12 hours ago | parent | prev [-]

The compiler also tells you that even if you cover all enum members, you still need a `default` to cover everything, because C enums allow non-member values.

YesBox a day ago | parent | prev [-]

Same. I go one step further and create a macro _STOP which is defined as w/e your language's DebugBreak() is. And if it's really important, _CRASH (this coerces me to fix the issue immediately)

creato a day ago | parent [-]

The "standard" (typically defined in projects I'm familiar with, and as of C23, an actual standard) is "unreachable": https://en.cppreference.com/w/c/program/unreachable.html

vlovich123 21 hours ago | parent | next [-]

That is not the same thing at all. Unreachable means that entire branch cannot be taken and the compiler is free to inject optimizations assuming that’s the case. It doesn’t need to crash if the violation isn’t met - indeed it probably won’t. It’s the equivalent of having something like

    x->foo();
    if (x == null) {
        Return error…;
    }
This literally caused a security vulnerability in the Linux kernel because it’s UB to dereference null (even in the kernel where engineers assumed it had well defined semantics) and it elided the null pointer check which then created a vulnerability.

I would say that using unreachable() in mission critical software is super dangerous, moreso than an allocation failing. You want to remove all potential for UB (ie safe rust with no or minimal unsafe, not sprinkling in UB as a form of documentation).

creato 19 hours ago | parent | next [-]

You're right, the thing I linked to do does exactly that. I should have read it more closely.

The projects that I've worked on, unconditionally define it as a thing that crashes (e.g. `std::abort` with a message). They don't actually use that C/C++ thing (because C23 is too new), and apparently it would be wrong to do so.

uecker 15 hours ago | parent [-]

You can use the standard feature with the unreachable sanitizer: https://godbolt.org/z/3hePd18Tn

skepti 17 hours ago | parent | prev [-]

For many types of projects and approaches, avoiding UB is necessary but not at all sufficient. It's perfectly possible to have critical bugs that can cause loss of health or life or loss of millions of dollars, without any undefined behavior being involved.

Funnily enough, Rust's pattern matching, an innovation among systems languages without GCs (a small space inhabited by languages like C, C++ and Ada), may matter more regarding correctness and reliability than its famous borrow checker.

zozbot234 17 hours ago | parent [-]

Didn't PASCAL have variant record types with a kind of primitive pattern matching already?

skepti3 14 hours ago | parent [-]

Possibly, I am not sure, though Delphi, a successor language, doesn't seem to advertise itself as having pattern matching.

Maybe it is too primitive to be considered proper pattern matching, as pattern matching is known these days. Pattern matching has actually evolved quite a bit over the decades.

YesBox a day ago | parent | prev [-]

C++ keeps getting bigger and bigger :D

Thanks for sharing

creato 21 hours ago | parent [-]

This is actually C and C++ has not done something similar AFAIK.

Fulgen 14 hours ago | parent | next [-]

https://en.cppreference.com/w/cpp/utility/unreachable.html

hn_go_brrrrr 17 hours ago | parent | prev | next [-]

Fortunately the major compiler vendors all have. Routing around the standards committee is getting more and more common.

TuxSH 14 hours ago | parent | prev [-]

C++23 does have std::unreachable (as a function), and its counterpart [[assume(expr)]]