6 ms·
Go's error handling is, frankly, terrible. It being better than some of the competition doesn't mean it's good. It's extremely verbose and relies on functions
by andrewzah 6y ago
Go's error handling is, frankly, terrible. It being better than some of the competition doesn't mean it's good.
It's extremely verbose and relies on functions returning a value OR an error. If I have an error in a function that returns a user or error, I still have to do "return &User{}, err". Or if I have no error, then it's "return user, nil". Ugly and verbose.
I really don't want to sound like a rust snob, but when it comes to errors and error handling, I only really want to use rust because it has Result<T, E> and Option<T> generic types. I don't have to do a million "if err ...", I can simply use ? inside a function that returns a Result<T, E> and kick it down the chain until I want to handle it. Then I can do a "match" statement on a Result to unwrap it and handle all cases.
To give an example, look at how succinct this is. [0] In that function I make 3 calls that could give an error and just put a ? on two of them, and I handle the last call manually with a match statement. Now compare that to this function in golang [1], where I have to make several calls that could error. (Admittedly this golang code could be better, but...)
Rust's system is not only more convenient but it forces me to handle error cases. That strictness is very annoying when I just want code to compile, but I'm sure it has saved me from plenty of bugs. With i.e. Ruby I ran into bugs at runtime quite frequently because I didn't realize I wasn't handling X or Y case. Golang at least has static types but it's still quite easy to forget to handle certain cases.
I say this having written a fair amount of golang code and a lot of rust code. Golang is not a bad language by any measure; I find myself reaching for it a lot.
[0]: https://github.com/azah/hangeul-rs/blob/master/src/lib.rs#L314 https://github.com/azah/hangeul-rs/blob/master/src/lib.rs#L3...
[1]: https://github.com/azah/caddy_logingov/blob/master/handler.go#L175 https://github.com/azah/caddy_logingov/blob/master/handler.g...
- flohofwoe 6y agoIsn't Rust's error handling essentially the same as Go's except that Rust has a bit of syntax sugar built around it? In Rust you still have a composite return value which bundles an error code with a value-payload, it's just wrapped in a Result<> type. So the whole thing is also just a convention, just as in Go, but built around Rust's language features. (PS: I agree that this sort of "explicit" error handling is vastly superior to exceptions, but in my point of view, Rust and Go are pretty much identical, at least when compared to exceptions).
- andrewzah 6y agoFor a Result<T, E> the returned type will either be an Ok(T) or an Err(E). Not both, so it's not a composite type like golang with defining a Tuple of (T, error). So you can then do a match statement like this: match maybe_error() { Ok(t) => {}, // we succeeded Err(e) => {}, // we did not succeed } Result itself is an enum with Ok and Err variants. [0] Some functions use Option<T> which will either be Some(T) or None. [0]: https://doc.rust-lang.org/std/result/index.html https://doc.rust-lang.org/std/result/index.html
- flohofwoe 6y agoOk, this requires Rust's "rich enum type" (whatever that's called), and some sort of generics, both of which Go doesn't have. But you can create the "gist" of this sort of return-based error handling even in C, by returning a struct which contains both an error code and a value-payload, it's just not as convenient (because C lacks generics), or as "safe" (because one can access the value even when an error is returned), but it still would be better than "old-school" C code which splits the error-code and success-value into a simple return value and an out-pointer, and arguably also better than C++ exceptions.
- throwaway8941 6y ago>"rich enum type" (whatever that's called) Algebraic data types, or sum types, or discriminated unions. GP also forgot to mention that Rust (among other languages with support for ADTs) force you to handle all possible values, so you simply can't forget to handle an error.
- steveklabnik 6y ago(They're called 'enums' in Rust though, your parent is correct.)
- thatswrong0 6y ago> so you simply can't forget to handle an error. This is such an important point, and I know people may think its outrageous that this could happen, but I work with a bunch of talented programmers, and still we've had the occasional bug crop up in our Golang codebase where we have a `val, err = doSomething()` without an ensuing `if err != nil { }` check which was missed by the programmer and by the reviewer. Or even better, we've had `val, _ = doSomething()` occur, the result of debugging to get around the unused variable compilation error. It's 2020. Just like nil-pointers, this is an issue that is for the most part _solved_ and shouldn't exist in modern languages. Our programming languages should be constructed with the fact that we're fallible humans in mind.