It's the equivalent of putting your head in the sand and pretending nothing else in the world exists.
Error handling in other languages is like confetti. “Pop! Surprise!” A disorganized game of “52 pickup” using an under designed grab bag of control structures. No thanks.
Go asserts that "error handling" and "main flow" (a.k.a. "happy path") code paths are equivalent, and that you can't ignore the bad stuff as you program against the good stuff. Error handling _cannot be_ an afterthought -- this is the path to fragile, even broken, code. If a programmer can't adapt to this model, then they're gonna have a bad time with Go, because with Go it's non-optional!
The compiler is one way that language invariants can be expressed and enforced. But it's not the only one -- another "layer" is convention and code review.
Concretely, something like
v, _ := f(...)
would _never_ pass code review at any reasonable organization.If you use the compiler to express invariants like this, then the language unavoidably becomes more complex. In some contexts, that makes sense! In others, it doesn't.
Yes...
> and that you can't ignore the bad stuff as you program against the good stuff.
No? I don't claim that -- at least in the sense that the compiler permits you to ignore errors if you want to. But that's not some death blow against the language. The compiler is one of many mechanisms that can be used to assert code quality.
Ignoring the second return value is also the easy case to catch. Ignoring an error-only return from a function doesn't produce a warning, and won't stand out in code review unless you adopt a standard of never writing void functions.
Go is a language for programminging "in the large" -- it explicitly defers a lot of stuff to layers "above" the compiler, like code review. If you don't buy that as legitimate that's fine!
> if you want to write happy-path code which ignores errors . . .
. . . it's obvious that's what you're doing in code review, and it's trivial to correct. Right?
---
> if you want to write happy-path code which ignores error
If you're doing this consistently you're going to have a lot of `value, _` i.e. underbar assignments in your code, and `_` isn't passing any code review process in any reasonable Go shop I've ever seen.
> Go asserts that "error handling" and "main flow" (a.k.a. "happy path") code paths are equivalent, and that you can't ignore the bad stuff as you program against the good stuff. Error handling _cannot be_ an afterthought -- this is the path to fragile, even broken, code.
Go doesn't assert anything about error handling. Go itself is extremely unopinionated about error handling. If you want to do something with the errors it returns you can, but if for some reason you want to "ignore the bad stuff as you program against the good stuff" it won't try to get in your way.
Idiomatic Go never just drops errors on the floor and ignores them, but it does involve a lot of just bubbling errors upwards without spending any time thinking about what happens in the error path. It's very easy to just write `v, error := foo() if error != nil { return error }` without considering if everything's actually in a sensible state if you return here or if bubbling the error up is even the correct thing to do. It _is_ the correct thing to do so often that anyone reading the code is also going to just glance over it.
If a programmer doesn't spend time thinking about error handling, Go does nothing to encourage them to do so. If they do, Go offers very little to help them do so. It certainly compares well in that regard to C and languages where every expression can throw an exception, but that is a very low standard.
The compiler will let you do this, but reasonable code review won't.
> Idiomatic Go never just drops errors on the floor and ignores them, but it does involve a lot of just bubbling errors upwards without spending any time thinking about what happens in the error path. It's very easy to just write `v, error := foo() if error != nil { return error }` without considering if everything's actually in a sensible state if you return here or if bubbling the error up is even the correct thing to do. It _is_ the correct thing to do so often that anyone reading the code is also going to just glance over it.
Not true? Keeping error handling in the same frame of reference as happy-path code makes programmers consider the two things equivalently, as peers. `if err != nil { return err }` is _not_ idiomatic Go! At a minimum you annotate the error. And that's often enough to be the right thing to do.
> If a programmer doesn't spend time thinking about error handling, Go does nothing to encourage them to do so.
Simply lifting error-path code into the same visible control flow as happy-path code is by itself a huge win for making programmers think about error handling. If you don't agree, fine. Many many others do.
To be fair, that is a good principle to follow ;) Non-void, especially pure, functions are much easier to understand in the context of an entire codebase.
> Ignoring an error-only return from a function doesn't produce a warning,
It does if you use the standard set of linters, which with Go you almost certainly do. Go has a strong linting culture, in part due to one being built-in. By the way, in vscode said standard set of linters is one click away in the config.
---
There are only 2-3 cases that I know of where failing to handle an error passes by silently. They are edge cases, not something you'll run into writing everyday code.
Go's method of treating them like any other value is the most honest approach I've seen. No one freaks out when a FizzBuzz program does different things with different categories of integers.
If your past experience involved people comparing error.Error() strings I’m sorry. That’s awful and I can see why that would be a nightmare