I use Go at my day job. Here's some random stuff I want to remember.
- Never use bare (aka naked)
returnkeywords with a function that returns values. Barereturns force you to run the whole function in your head to figure out what's being returned, and humans are terrible at that. - Put the type on every function parameter, it's clearer:
func(id int, iterations int), notfunc(id, iterations int) - Don't use
gotoexcept to break out of (or continue) a nested loop, and that should be exceedingly rare. - Always use the company
GOPROXYif you're doing work stuff. - Avoid single-letter variable names as much as you can, unless it's a tiny loop. Three- or five- or even eight letter variable names ain't gonna kill anyone, sheesh.
- Since Go doesn't have enums, use package level constants instead. You can newtype them if you're afraid folks will mix them with other values of the type, but it comes with the cost of having to cast the newtype to the base type to compare them.
- Go tooling has backward defaults. Feed any command
./...to have it operate on everything like it ought to have in the first place. - The folks who create Go don't care about ergonomics in tooling -- don't bother asking for or suggesting improvements, they will be interpreted as a sign that you are not a Real Gopher™ and dismissed as unnecessary. Bash, Makefiles, etc. are the workaround.
- Test functions start with
Testand take a*testing.T - Benchmark functions start with
Benchmarkand take a*testing.B, iterate from0tob.N - The one place you can use an underscore is in the name of a test testing an unexported function
Test_someFunction - Use
t.Fatal[f]to print/return an error in a test.t.Error[f]will not return from the function. - Use a
TestMainfunction if you need to set up / tear down stuff for the tests (only one per package). *testing.T.Cleanup()is likedefer, but you can pass the*testing.Tto other functions and callCleanup()there and the cleanup happens at the end of the test.- During a test, the current directory is the package directory of the current package.
- Always put test data in a directory
testdata/relative to the package directory of the test that uses it. - Avoid creating package-level variables.
- Unit tests go in the same file as what they're testing.
- Make a
packagename_test.gofile in the same directory for integration tests, but it will bepackage packagename_testand you'll have toimport full/path/to/package - Use
cmp.Diffandcmp.Comparerfrom thegithub.com/google/go-cmp/cmppackage to compare structs, maps, and slices in tests. - Use
errors.Isanderrors.Asto check error types - Use
reflect.DeepEqualfor comparing maps and slices - Interface values are wide pointers. If both pointers are
nil, then the interface value isnil, but if the interface value is anilof some type (the type pointer is notnil), then the interface value is notnil. This is why code that uses interface values should be written so it works withnilvalues...because it's hard to tell if the interface's value (via the value pointer) isnilor not. (The only way is through reflection).