Trying out Go
15 May 2025At some point in the late-2010s I went to a Go programming language event and for reasons long forgotten decided that Go was not the language for me. Maybe they were showing some highly esoteric things that to me looked over the top but whatever it was six years may have changed whatever likely dogmatic things that were on my mind at the time. I had not intended to look at Go so soon after trying out Rust but while at work debugging some GitLab CI build scripts that had a testing turn-around times of half an hour or so I spent some intervals going through Getting started with Go and was sold by the end of the day. The way Go enforces coding standards is certainly a sore point but overall it felt better than getting up to speed with Rust.
Language features
Having very recently been using Rust the constant thought in my mind when trying out Go is how it compares, not only to Rust but also other C-like languages I have used in the past. There are some things I prefer in Rust but overall I felt that bit more at ease with Go's language choices although in the grand scheme of things they are not that much different from each other. Suppose the most notable thing is Go being a garbage-collected language so it does not have some of the pit-falls that result from Rust's ownership & borrowing methodology which ranks as Rust's most newbie-unfriendly trait, and it is certainly something that was the root cause of much of my early difficulty with it.Enforced bracing style
Whereas Rust's linting toolrustfmt
wants K&R style bracing with Go trying to use other styles results in a compile error which almost certainly was a reason I avoided the language in the past.
It is the style I have had to use professionally since 2015 which made me develop a tolerance for it but for personal projects that were for my own enjoyment it remained a complete no-no.
The official reason is to make semicolons at the end of statements optional but the real reason is more likely a heavy-handed approach to do away permanently with any debate on bracing styles.
Due to having done little if any personal C programming projects since Covid lockdown this is something that does not bother me as much as it used to.
Partly-enforced variable name styles
When writing modules things that start with a capital letter are exported whereas things that start with a lower-case letter are private, which is in line with Go's overt preference for camelCase and PascalCase. These happen to be my preferred styles although I have doubts about capitalisation rather than something more explicit to indicate what should be exported and along with bracing it seems another case of an excuse to enforce specific styles. With Rust not using snake case results in a compiler warning that can be suppressed but Go aside from the capitalisation of the first letter is relaxed about different styles of variable names.Variable declaration
With Go specifying the type of a variable is optional and if it is specified it comes after the variable name just like it is in Rust, the one difference is there being no symbol in between the name and type which I marginally prefer. An interesting thing is Go using both Pascal style semicolon-equals for variable declaration as well as the much more common equals on its own, and the Pascal style is an implicit declaration so the two lines below are considered equivalent:var x = 5 y := 5
Note that semicolon-equals cannot be used as assignment to an existing variable due to the implicit declaration:
y = 5 // Ok y := 5 // Error!
I am unsure whether this is a good or bad thing but I like it. Variables are automatically initialised to zero or empty which I think it is a lot nicer than in Rust where uninitialised variables are not permitted, which is a pain when it comes to filling out structures piecemeal. Another way that Go is stricter is unused variables being an error rather than a warning, at least by default.
Closures
Closures are something I came across in Haskell although I think at least at the time it was under a different name, but the concept amounts to returning functions rather than variables although I suspect Haskell's variant is much more flexible and certainly more concise. One basic use of these is as a form of function pointer which is something I have made extensive use of in the past when writing server software, although the nature of my professional work means that I don't do fancy programming stuff like I did at university.Built-in web functionality
What sold me on Go was it having built-in web-server functionality so I am seriously considering using it for a reimplementation of my website's technical infrastructure. Moving from PHP to Flask was under serious consideration but net/http has a controller that seems very close to how Flask does things and along with the html/template view templating this is exactly what would be needed for any future rebuild. As an example take this template:<h1>Posts</h1> <ul> {{- range . }} <li><a href="{{ .Link }}">{{ .Title }}</a> {{- end }} </ul>
Using {{-
rather than {{
avoids empty lines in the output.
Together with the program below that uses the template to render an array into an unordered HTML list, this is practically everything needed for a new back-end.
package main import ( "html/template" "net/http" ) type Post struct { Title string Link string } func handler(w http.ResponseWriter, r *http.Request) { info := []Post{} info = append(info, Post{Title:"Title 1", Link:"?post=1"} ) info = append(info, Post{Title:"Title 2", Link:"?post=2"} ) html,_ := template.ParseFiles("template.html") html.Execute(w, info) } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) }
The main selling point is a Go implementation needing just the deployment of a single statically-linked program whereas Python & Flask would need messing around with things like virtual environments on the server and if I had to go down that road containers would come into consideration. In practice would want to use a Unix socket rather than TCP socket but doubt working out how to do that will take long.