I have been hearing about Go for long time and along with Rust is one of the two new programming languages that seem to be gaining some attention in the last years.
After learning Go it seems to me a good alternative to other programming languages because is simple, beautiful, hassle free, and fast compared to everything else that is not C, C++ or Rust. Its simplicity is really appealing because you can start small and grow as big as you want.
Go is ideal for containerized apps and websites. Runs faster than other popular alternatives for web, with a small memory footprint, and their executables have no dependencies. It’s blazing fast to compile, so iterating with new versions and deploying is almost as fast as interpreted languages.
Being so simple to understand, it requires a very small training to be able to be productive in Go.
Comparing Rust with Go
- Go is developed by Google. Rust is developed by Mozilla.
- Go use case is a more practical C or more scalable Python. Rust is a high performance alternative to C++.
- Both have garbage collection.
- Both are compiled.
- Go is productive. Rust is fast.
In short, Rust would be a better option where speed is key as it is as fast as C++; Also, it has some features that aim to have cost-less abstractions, so it looks promising for big complex projects like browsers. It should be able to deliver the same speed as C++ with less complexity on the code.
Five reasons to use Go
- Simple and beautiful: Go is easy to read and easy to write
- Compiles fast: As far as I know, Go is the fastest language on compiling times. It’s one of its main purposes. It also includes an “interpreter”, so you can run go programs from source to avoid compiling while developing.
- Static typing: When a program grows large, having static types may help a lot, also on safety purposes. Go is statically typed, so your programs can grow while staying safe that they will run as you expect.
- Explicit but terse: Go language is explicit, so the meaning is clearly conveyed in the programs. But at the same time is terse, so we don’t spend much time writing Go.
Is Go better than Python?
While Go does not have the flexibility and magic that Python has, still has basic primitives for flexible arrays and maps (dictionaries) so with Go we don’t lose as much proficiency as in other compiled languages.
But Go is way faster than Python, so if our program has to do custom complex calculations, Go can be 30 times faster than Python. But beware, as Python links their libraries to plain C, in some (very) specific use cases it could beat Go or other languages.
There’s no much difference on the development cycles from Go to Python. Go also has the ability to run the programs on the fly, so the sequence of code-try-code is equally fast.
Go produces final binaries for the platform, and statically linked. So, for distributing, you don’t need to distribute sources (this can be good or bad depending on your point of view). But you also have to build different binaries for different platforms. Because is statically linked, there’s no need to account for the different Linux distributions, so the same binary should run across all Unix flavours that support ELF format and run the same architecture.
For distributing on Windows, Go could be easier as just produces an executable and runs across many Windows platforms while on Python you have to care on packaging for Windows and test it properly; or tell the user to install the whole development stack which is a hassle.
The main disadvantage of Go vs Python is that Go tries to statically compile everything, so the behavior of code is set at compile time. There are Go interfaces which can help creating this kind of “magic” abstractions that change behavior depending on the scenario, but aside of that it’s a bit limited. In contrast, Python is much more flexible.
Is Go better than C++ or Java?
While C++ and Java are more feature-rich, Go is simplified and more productive. Also Java tends to be memory-hungry, so Go it’s useful to run programs in constrained memory and disk requirements.
Because Go statically links everything inside, its executables will be bigger than their C++ counterpart, but still way smaller than Java as you have to carry the JVM and libraries which use a lot of disk space. This makes Go an excellent candidate for containerized applications.
The downsides of Go is the lack of abstractions, and is slower than C++; being more or less as fast as Java (but still a bit slower in some scenarios).
Go is strongly opinionated
While learning Go for the first time I found a lot of things surprising. For me Go is the plain old C language with a new Pythonic style. I like both C and Python a lot so I see a lot of influence from both languages in Go.
When designing Go they weren’t scared of breaking the rules, it is clear that they have a strong opinion on how things should be done. In the same sense that Python wanted to specifically ditch the braces for blocks and the “switch” statement, they had clear that they don’t want classes (on the usual OOP approach) and they don’t want exceptions.
Unlike Python, it doesn’t use whitespace for blocks and uses the classic braces and has an extended switch statement.
The braces don’t need any explanation, but the switch deserves a mention. The main problem of a switch statement is, by default, it follows from one case to the following, causing unintended bugs.
In Go they solved it by going the other way around: By default each case is independent unless you add the keyword “fallthrough”. This makes this construct less bug-prone and terser in the common case:
There are also special types of switch: with no condition so all conditions are in cases; for variable type detection; and finally for processing many asynchronous events at once.
As said before, there are no exceptions in Go. You’re expected to return the error using a conventional return statement, so the common approach is to return a tuple of (value, error) or (value, ok). This comes from C where we used to encode errors in the return value. But Go makes this way simpler by allowing tuples of values to be returned.
It also has an error primitive that can be used easily to convey error messages as text, and it can be extended to your needs.
This means that your code should be checking for error codes explicitly. Failing to do so means that the code will continue running using a default value instead. It does not fail the execution.
Go programs can fail completely as well and stop execution. This is known as panicking. Panic can be started from an internal call or manually by the programmer by using the “panic” function. So instead of throwing errors, you can just panic. In this case, checks are caller responsibility.
Now functions have two ways of exiting, returning and panicking. So they added a “defer” statement to compute cleanups at the end of the function. This is useful because it is run regardless of how or when the function exits.
Panicking unrolls the stack, calling all defer statements in the way. It is still possible to avoid the program from crashing by using the recover keyword. This actually looks like a flavor of try..catch, but is not recommended in Go. Although less performant than error codes, it can be clearer or easier to reason in some cases.
Going back to classes and object-oriented programming. Go does not have classes but it has some of the object-oriented ideas implemented, again, in a more flavored C style.
They use structs and interfaces. Structs are like regular C structs, so no code, just data. They can be inherited in the same sense as in C, stacking one in top of the other. This is called “struct embedding” and Go adds syntactic sugar to help this:
Multiple embedding is possible, just stacks the structs one in top of the other, much in the style of C. So no diamond problem, if a name appears twice, it will be stored twice.
The code for those is held outside of the struct, by defining functions for types. Much like Python, self/this is declared explicitly:
And then, there are interfaces to be able to write code that manages diverse types at once. It might resemble to Python duck typing, Java interfaces, C++ virtual functions, etc. But it is a thing on its own.
Interfaces define a set of methods that must exist in a type. A type does not declare if adheres to any interface. The fact that the type has all methods is enough to be able to use it for the said interface. So in this sense, it resembles duck typing.
And finally, Go is one of the few programming languages that I know of that is compiled and supports UTF-8 natively.