A few months ago I switch to working in Go. Before that, my main language was Python for many years. The change to Go has been very smooth, without any major surprises or stumbling blocks. This may partly be because in the past I have also worked in both C++ and Java. Even so, Go (the parts I have used so far) is quite straightforward.
Before I started in my new role, I read through The Go Programming Language, which is quite good. Otherwise I have mostly used online resources when I have needed to learn how something works. Both A Tour of Go and Go by Example are good.
Here is a list of things I noticed with Go since I started working in it:
Observations
Error checking. Before starting to use Go, I thought always having to check for errors after a function call would be tiresome. But this has not been a problem – I got used to it very quickly, and it doesn’t bother me.
Methods. The Go way of creating a method by attching it to a type (typically a struct) is unusual if you are used to classes. But it is quite convenient and easy to get used to.
For loops. The only kind of loop in Go is a for loop, and it comes in many flavors. The first time I saw a for loop over a boolean condition I was a bit puzzled, before realizing it is just a while loop. Looping over ranges is convenient, and I like that you can get both the index and the element.
Declaration and if. It is common to declare and assign to a variable, and check the result of it with an if-statement on the same line. This is something that I am still not used to. I takes me extra time to figure this out when reading code, even though the goal of compact code is good.
Capital letter. Names beginning with a capital letter are exported outside of the package. If they start with a lower case letter, they are local to the package. It is good to limit what is exported, but I would have preferred to have e.g. a public
keyword instead. It is clearer, and you avoid cases where you are constrained in the names you use.
CamelCase. In Go, it is idiomatic to use CamelCase
for names. I find this harder to read (especially for long names) than snake_case
that is used in Python
Multiple return values. Being able to easily return multiple values from a function is really useful.
Named return values. You can (but don’t have to) name the return values from a function or method. This can make the code clearer. But it still looks a little bit odd when there is just a return-statement, even though the function returns many values. You also have to be aware that if a return value is not assigned to in the function, its default value is returned. You don’t need to use them, you can still just return the values themselves. If you mix these two ways in a function it can look a bit odd.
Unused local variables. An unused local variable is a compilation error. The idea is good, and it can help catch bugs. But it can be a bit of a pain when editing. If you comment out something temporarily, there are often local variables that now become unused, so they have to be commented out as well. This becomes unnecessary busywork. It is also interesting that unused function arguments are not compilation errors.
Anonymous embedded structs. A struct can contain fields, but it can also contain another struct without a name (i.e. anonymous). The fields of the embedded struct then become accessible by their respective field names, which is useful. But this confused me the first time I saw it. More in this Stack Overflow answer.
Defer. Using defer
to have a clean-up function run before returning from a function call is nice and convenient.
:= and =. Declaring and assigning to a variable is done with :=
, whereas assigning to an existing variable is done with =
. This is fine, but I once caused a tricky case of shadowing a variable because of this. Typically the error status variable is called err
. Once I changed some code in an if-statement, and used :=
instead of =
, so instead of assigning to the existing err
variable, I created a new one that went out of scope outside of the if branch. So I thought the existing err
variable would be set, but it was not. I scratched my head a bit before figuring that one out.
Conclusion
Overall, Go has been very easy to get into. Maybe that is partly because I have previously worked in both C++ and Java. Even so, much in Go is straightforward. In the past years I have learnt both Clojure and Erlang. They are both functional languages, which is different, but they also have quite a lot of special syntax that makes reading code in them hard in the beginning. In contrast, almost everything in Go has been intuitive and easy to understand.
Granted, there are many parts of Go that I haven’t used much yet, like goroutines and channels. But it is nice that you can do a lot with just the basic parts. And even though I enjoy working in Python, it is really nice to be back in a statically typed language again. It makes navigating and understanding an existing codebase so much easier. I can look at the types going in to, and being returned from, a function. And the IDE can show me all usages of a given construct.
It takes a while to get to know a language. You can get a feel for it by trying it out for a few days, but I think you get a much truer impression of it if you work in a large codebase for at least a couple of months. However, in the end the language for me is secondary. When I think back at the products I have worked on, I am not thinking about the programming language I used. Instead it is the application I remember, both its structure and its functionality. The language is just a tool. Right now for me, Go is a good tool.
Top comments (0)