4 ms·
Passing just the error up the stack is also quite easy in Golang, just use a named error return value and assign to it: func test() (result interface{}, er
by ThePhysicist 5y ago
Passing just the error up the stack is also quite easy in Golang, just use a named error return value and assign to it:
func test() (result interface{}, err error) {
_, err = someOtherFunction()
}
The thing is, you probably want to handle the error eventually, and that is just as verbose in Rust [1] as it is in Golang (IMHO). One can of course debate if it's more pleasing to use a "match" operator than an if/then/else loop to handle errors, but to me the difference is marginal. BTW one could even mimick Rust's type-based errors in Golang by returning a single interface{} type that's either a result or an error and then using a type switch, but that seems just silly and I have never seen that in the wild.
1: https://doc.rust-lang.org/rust-by-example/error/multiple_error_types/define_error_type.html https://doc.rust-lang.org/rust-by-example/error/multiple_err...
- simiones 5y agoThe difference is when a function has more than one line. In go, you constantly have to `if err != nil { return }` even with named return values. This is the part that breaks up the flow constantly and hurts readability. In Rust, the ? macro also handles this.
- samhw 5y agoPrecisely. As soon as you go beyond a ten-line toy program, you'll hit this problem, and in my experience it's almost universally acknowledged to be a problem with Go. Rust's '?' sugar effectively replaces the whole `if err != nil { ... }`, meaning it wasn't really honest to list Rust as another language that proves that error handling just has to be verbose and repetitive.
- ThePhysicist 5y agoSee my comment below. I never use this pattern personally, just wanted to point out that it exists. In general I think passing up errors through the call stack without adding context to them or handling them is a strong anti-pattern. I think many people in the Golang community see this similarly. And by passing an error up the stack with "?" you haven't magically "handled" it, so I don't think that's a fair point.
- erik_seaberg 5y agoA stack of ten functions wants to handle the error once. Returning it nine times is exactly the type of mind-numbing chore that computers do much more quickly and reliably than people, and I will never understand why so many want not to automate this.
- bregma 5y agoNow that computers are starting to write software it will eventually matter less. Until then, code is written by and for people, not computers.
- zohch 5y agoAll the code that actually runs on CPU is written by a computer, when it executes the compiler or runs an interpreter. And most of them don't need advanced machine learning functionality to implement an error handling syntax which is significantly less verbose.
- ThePhysicist 5y agoReturning an error through nine levels of functions without doing anything with it anywhere in that call stack would indicate a strong problem with the code architecture to me. Personally I rarely pass on errors "as they are" in Golang anymore, as that makes debugging quite hard. A function receiving an error should either handle it (e.g. by recovering from it or logging it), or at least add relevant context to it so that the user can figure out what's wrong. I used to like passing errors up the stack like you describe, but this invariably leads to hard-to-debug programs that terminate with unspecific errors like "i/o error", leaving it to the user or developer to figure out in which exact branch of the 9-layer call stack that error originated. If your error handling is exception-based and if your interpreter/runtime generates tracebacks you can do that, but not if you explicitly handle errors within the regular control flow.
- samhw 5y ago> this invariably leads to hard-to-debug programs that terminate with unspecific errors like "i/o error", leaving it to the user or developer to figure out in which exact branch of the 9-layer call stack that error originated You might want to add a caveat to 'invariably', something like: 'provided you're using a language where errors are modelled as one big mashed-together string' ;) If we assume the basics of how Go is designed are an invariant, I think the best change would be adding syntactic sugar to return a struct satisfying the error interface, annotated with the (statically encoded) symbols and line numbers, and then also the arguments for those function calls. I have never seen a Go codebase whose hand-written 'contextual' error handling blocks provide even this, let alone more useful information. If you have, I'd be interested to hear about it!
- samhw 5y agoI'm not sure how that code block is meant to work. You still need to check the value of error and conditionally return, unless you're suggesting that most functions only contain one function call.