Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Say a function has some return type Result<T, E>. If our only error handling mechanism is Err(e) then were restricted to E representing the set of errors due to invalid arguments and state, and the set of errors due to the program itself being implemented incorrectly.

In a good software architecture (imo) panics and other hard failure mechanisms are there for splitting E into E1 and E2, where E1 is the set of errors that can happen due to the caller screwing up and E2 being the set of errors that the caller screwed up. The caller shouldn't have to reason about the callee possibly being incorrect!

Functional programming doesn't really come into the discussion here - oftentimes this crops up in imperative or object oriented code where function signatures are lossy because code relies on side effects or state that the type system can't/won't capture (for example, a database or file persisted somewhere). Thats where you'll drop an assert or panic - not as a routine part of error handling.



You shouldn't pass invalid values to a function. If a function can return some sensible value for some input, then the input is not invalid - even if the return type is an error by name.

Ideally, you can constrain the set of inputs to only valid ones by leveraging types. But if that's not possible and a truly invalid input is passed, then you should panic. At least that's the mental model that Rust is going with.

You do lose out on the ability to "catch" programming errors in subcomponents of your program. For example, it's extremely useful to catch exceptions related to programming errors for called code in response to a web request, and return a 500 in those cases. One could imagine a "try" "catch" for panics.

The thing is, it takes a lot of discipline by authors to not riddle their code with panics/exceptions when the language provides a try/catch mechanism (see C# and Java), even when a sensible error as value could be returned. So Rust opts to not introduce the footgun and extra complexity, at the expense of ungraceful handling of programming errors.


> Ideally, you can constrain the set of inputs to only valid ones by leveraging types. But if that's not possible and a truly invalid input is passed, then you should panic.

But how can the caller know what is "a truly invalid input"? The article has an example: "we unfortunately cannot rely on panic annotations in API documentation to determine a priori whether some Rust code is no-panic or not."

It means that calling a function is like a lottery: some input values may panic and some may not panic. The only way to ensure that it doesn't panic is to test it with all possible input values, but that is impossible for complex functions.

It would be better to always return an error and let the caller decide how to handle it. Many Rust libraries have a policy that if the library panics, then it is a bug in the library. It's sad that the Rust standard library doesn't take the same approach. For println!(), it would mean returning an error instead of panicking.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: