I think you’re comparing C’s error handling with something totally different. And again, unless you’ve used it in a real-world project, this is all speculation.
You don’t have to use Rust for that, in fact this is not unique to Rust at all. Vlang is one example among many, and functional languages usually have the same patterns.
Worse, while it enables safe code, it does little to encourage it. In practice, most error codes were ignored. (Who checks the return code from printf()
, for example? Just about no-one!)
I don’t know, but you can’t ignore it in Rust. That’s the point. Errors are not codes either, they can be any type and hold data.
Option
and Result
are union types. They’re literally defined like this:
enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}
If a function gives you an optional (Option
), that’s exactly like Kotlin’s nullable values. You must handle the None
(equivalent to null
) case. Ignoring None
in this sense is basically calling unwrap()
, which gives you the result if it’s Some
, or panics if it’s None
, !!
is the equivalent of unwrap in Kotlin (although usually with smart casts Kotlin can know if you’ve checked it before).
If it returns a Result
, you can do the same as well to “ignore” an error.
Using unwrap()
means you’re prototyping and probably do not care much, or you’ve already checked the invariants and are sure this will never be a None
or Err
(if it is, it’ll panic, signaling a bug).
it ‘fails safe’, unlike the explicit case where doing nothing means it ignores the error and ploughs happily on, which can do far more damage.
It panics.
I probably said this 20 times now. Ignoring means panicking, which is Rust lingo for crashing and burning everything down.
Again, in 99% of cases, using unwind
runtime (which is default), this can be caught exactly like exceptions. The failing code will not be rescued or resumed (otherwise we’re back to C, what’s the point of Rust?), but you can still save the rest of your program and handle this accordingly. But catch_unwind
is there for very niche cases, otherwise everything follows the Option
and Result
patterns.
But wait…
I don’t know why are we having an argument about Rust’s error handling. It’s nothing new, functional languages have always done it and it proved itself more reliable than exceptions. That doesn’t mean exceptions are bad, they’re still a whole lot better than error codes that can be happily ignored or misinterpreted while continuing to execute the code (leading to corrupted data, security vulnerabilies… hell, even nazal demons) and provide very little information.
I never thought I’d have to argue with anyone about Rust’s error handling nor the promise of zero cost abstractions, because anyone who took the language seriously (even slightly) will know that these are the actual pros of the language.
I’m not a fanboy of anything, I love exploring new ideas and languages, Rust is only one of them. But truth has to be said. Rust does have issues that actual users do know. We love the borrow checker but it can be annoying. Fearless concurrency is a real thing, but if you screw up… your code won’t build and compiler errors can be unwieldy and hard to understand (which is good… but pretty annoying). Dynamic dispatch still has tons of room for improvements, many parts of async support are still unstable, traits can and should be able to do more, many limits can be lifted, and a lot more.
All of these things are being worked on to make them easier, more approachable, and more predictable, because this isn’t C or C++, and changes are not too scary for the language (yet, they still don’t break old crates, through the “editions” feature).