We’re big gophers here at Ninja Software, and the majority of our back-end services use Google’s Go programming language. Go gives us the perfect balance of simplicity, productivity and performance.
Of course its hard to not be tempted by the dark side. That is, the Rust programming language and its technical depth. This language is often compared to Go (they can both write web servers and are both called systems languages!). Of course, people say that you shouldn’t compared the two (Rust is for low level programming!)
Some people insist that they’re completely different, as Rust is focused on low level primitives, memory management and zero cost abstractions. A replacement for C/C++. Go is focused more on higher level apps like microservices, web servers and other networking and webscale systems while Rust is about optimising your IO and memory management.
Yet they definitely have obvious similarities. They are both new programming languages, AOT compiled, generate static binaries and both capable of building what we build at Ninja Software, namely systems level applications that run lean, are concurrent and scale without issue.
Go receives a lot of hate, because it lacks a lot of the cool new features, bells and whistles that people have come to expect over the past few decades. Generics, traits, functional programming, immutable by default. Things that Rust has by default.
So what’s not to love?
Slick, sleek tooling
The Go creators (and community) had a razor sharp focus on tooling. Go’s
help and friends all come together to create a pleasant, unified and consistent experience when developing with Go. A lot of work was spent re-implementing C libraries to get solid cross compatibility to work. The standard library was rock-solid from the start, and the community ideologically sticks to this approach.
I will cover my thoughts on Go modules in a future blog post.
Rust has been catching up, and has definitely overtaken Go when it comes to package and dependency management. Cargo is an amazing piece of technology, and Go modules reference Cargo as an inspirational implementation of dependency management. The other aspects of Rust, such as the cross compatibility story, static linking and others will catch up eventually.
The borrow checker and cognitive overhead
I’ve spent some time in Rust land (your typical Saturday morning software developer playtime) and have been really impressed with what it offers. Traits are like interfaces, but better! Rust has generics! Wow everything is immutable by default!
Fearless concurrency! Easy refactoring!
It all sounds pretty amazing, until you sit down and try to program in it. There’s a lot to digest and account for. The borrow checker will beat into you the (in)correctness of your code. You have to unwrap results and remember the lifetimes of your variables.
At the end of a session, its exhausting. The language, syntax and compiler does so much and forces you to really think about what you’re telling the program to do. There’s just something “academic” about this approach.
Despite the barrier to entry caused by the borrow checker, it absolutely is amazing tech, and it’s what makes Rust amazing. Once you get your head around it, you’ll realise this is Rust’s secret sauce. Smart pointers are an awesome way to handle memory management without having to implement a potentially latency heavy garbage collection system.
Correctness versus Business Value
Rustaceans often focus on “correctness”, an ephemeral term about an application performing exactly as designed, without side effects.
Haskellers have similar aspirations towards purity, as do most functional programmers.
At Ninja Software, we recognise that our language of choice for our stack is ultimately pretty boring. Without a doubt it lacks features that some have come to expect. It ruthlessly prevents you from working out creative ways to get around its limitations. Nil pointers will strike if you don’t account for them. These are all the result of a compromises that were made to keep the language simple.
As the CTO of Ninja Software, selecting the stack that all other developers will use in our organisation is a big task. I personally feel the Go approach results in a language that produce applications that ultimately produce value for a business that has to move as fast as the web, while being robust enough to be reliable, fast enough to meet our performance requirements and is quick to onboard new staff.
Sometimes, mostly correct is the right answer.
Fingers crossed, we’ll have Generics for Go 2.0!