3 ms·
It's also suprisingly easy to mess up error returning. I've accidentally done things like: a, err := run() if err != nil { b, e2 := cleanup() if e2
by atombender 8y ago
It's also suprisingly easy to mess up error returning. I've accidentally done things like:
a, err := run()
if err != nil {
b, e2 := cleanup()
if e2 != nil {
return err // Whoops
}
return e2 // Double whoops
}
Since you generally test the happy path more diligently, subtleties like these tend to show up later than desirable (compile time would be the most desirable, of course).
The := semantics is partly to blame here, as well as Go's lack of laziness around unused values. Go is curiously strict about unused variables, which never hurt anyone, as opposed to unused values, which absolutely can hurt. For example:
a, err := foo() // This err is never used
b, err := bar()
This is much worse! And the compiler doesn't complain. Fortunately there's a linter called ineffassign that tracks ineffective assignments, and it's part of gometalinter and golangci-lint. But not "go vet". And there's no linter that can detect the first example that I gave.
Shadowing is a pet peeve of mine. Go is one of the strictest, most opinionated languages in mainstream use, but it's amazingly lax and unopinionated in certain areas. "go vet" can check for shadowing, but doesn't do so by default, and of course it doesn't test the subtype of shadowing that := causes. Shadowing is usually a bad idea, but Go encourages it through its := assignment semantics.
- sickcodebruh 8y agoGo’s lax attitude about shadowing jumped right out at me, too. Coming to Go recently from TypeScript, where the combination of the compiler and tslint will aggressively bark at me, was jarring. Coukd you point me towards any crash courses on using the Go linters you mentioned to increase safety of my code? I’ll look them up, too, but if you have any resources handy or think there’s work for me beyond RTFM, I’d appreciate it.
- atombender 8y agoFirst, I recommend golangci-lint [1], just because it's much more performant (and designed to be from the start) than gometalinter. It uses much of the same code. Some of the more useful linters, like deadcode, can be a little too slow to run in an editor like VSCode. We run the full linter list as part of our CI tests, and then in development we run it with "--fast", which runs only the fastest one. I've disabled megacheck, which didn't work with Go modules the last time I tried it. Among the prominent ones I use, all enabled by fast mode, are ineffassign (as mentioned), deadcode (detects unused code), govet (runs "go vet"), errcheck (checking for calls where you ignore the error result), structcheck (finds unused struct fields), varcheck (finds unused variables and constants), typecheck (this does the same thing as "go build", basically), and unconvert (detects unnecessary type conversions). The slower ones that are worth turning on are staticcheck (performs lots of static checks) and unused (checks for unused variables etc., but is slower than varcheck and structcheck, apparently). [1] https://www.github.com/golangci/golangci-lint https://www.github.com/golangci/golangci-lint
- sickcodebruh 8y agoThank you! This is great. For all of Go’s warts, it lived up to every single hope and expectation I had for it in this last project. I’m looking forward to improving it with these suggestions.