a, err := f()
if err != nil {
return
}
a, err = f()
This will compile.Yeah, it might be nice if it were integrated into the language but on the overall cost/benefits analysis of my actual costs & benefits rather than merely aesthetic ones, this one doesn't actually factor very high for me because using errcheck is easy. And I supplement all languages I use seriously with aftermarket checkers so it isn't like this is special pleading for Go, either. I don't trust any language out of the box any more.
First, it requires having a variable in the first place, if you call a function for its side-effect and don't remember that it returns an error, Go won't tell you.
Second, Go only errors on dead variables, not dead stores, since conventionally the error variable is "err" you can easily forget to check one of them, and Go won't say anything because the err variable was read in one of the other checks.
It's even worse if you're one of the weirdoes which uses named return variables, because named return variables are always used:
func foo() (v int, err error) {
a, err := bar()
b, err := baz()
v = a + b
return
}
compiles just fine. But at least you're returning the second error. No such luck if you're using named return variables for documentation: func foo() (v int, err error) {
a, err := bar()
b, err := baz()
return a + b, nil
}
go also has nothing to say about this.Honestly the thing that I think lacks more is macros. With macros it would be trivial to write
a := Must!(f())
that* assigns last return value to err
* calls return with that error if it is not nil
b, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read %s: %w", path, err)
}
is so much better than b := Must!(os.ReadFile(path))
because when things go wrong, I have exactly the right amount of information I want. Assigning to err magically (it's not even mentioned in the source code) is exactly the kind of thing that'll turn out to be the cause of a subtle bug 6 months later. Why not spend the a couple of extra lines thinking about the error when the context is still in your head?Additionally I like how error handling acts as a visual delimiter, especially when you use meaningful fmt.Errorfs as strings are usually highlighted with a different colour, making it easy to quickly jump through code.
Really? Because `ReadFile` already adds the path to the context, so what you actually get is
read /tmp/foo: open /tmp/foo: no such file or directory
which is more confusing than "the right amount of information".Furthermore nothing precludes `Must!` taking a prefix and wrapping automatically, does it?
> Assigning to err magically (it's not even mentioned in the source code) is exactly the kind of thing that'll turn out to be the cause of a subtle bug 6 months later.
What bug? It's assigning and returning, the only situation where you'd have "a subtle bug" is if you didn't check the previous call and overwrote its error, which is exactly what you get with the code you propose.