Go is gaining prominence as one of the languages that programmers love the most. The main reasons for that popularity are quite visible to anyone with some familiarity with the Go – it’s simple in a good way, easy to use, multithreaded, fast, and very powerful. Let me show you some basic things and tell you why I really like working with this technology and how it compares to Java.
What is the Go programming language and how it came to be?
The Go language was created in 2007. The main “culprit” behind its birth was Google – the corporation’s developers were dissatisfied with the existing coding languages (especially C++). The basic idea behind Go was to combine the best aspects of other technologies, like C or JavaScript, while eliminating some of their problems at the same time.
Go is a statically typed programming language, with very good readability and usability (in these respects, it resembles Python and JavaScript). It’s characterized by high-performance networking and multiprocessing – in fact, that second feature was one of the main reasons for the language’s creation.
It’s worth mentioning that Go is one of the most loved programming languages – in the StackOverflow’s 2020 Developer Survey, it took 5th place with a score of 62,3% (a year before, it was 10th, which means it gained a lot of positive attention and praise in the last twelve months).
Go programming language – main advantages:
- Easy to use
It was one of the most important things for Go’s developers and it’s the reason why they’re so stubborn when it comes to further development of this solution – they’re reluctant to add new functionalities when there are other ways to achieve the same effect.
- Packet management
Go consists of several different packets and you can manage your application through them – you add new functionalities by importing packets.
- Powerful standard library
The language has a very powerful and functional standard library, with packets for the most common coding scenarios.
- Static typing
Go is a statically typed programming language, which means the compiler checks the type conversion and compatibility. Therefore, you can avoid problems that often arise in the case of dynamically typed languages.
- Unit test support
Go supports unit testing and it’s very easy to write both the tests themselves and any needed documentation.
- Self-contained
Go is independent of the platform and can be used on any server because the code is compiled and converted into binary.
Getting started with Golang – syntax and examples of use
Let’s talk a bit about syntax and some examples of how the language is used. A simple “Hello, world!” in Go looks like this:
1
2
3
4
5
|
package main
import “fmt”
func main() {
fmt.Println(“Hello, world!”)
}
|
As you can see, it’s pretty simple. You only need to import one packet – “fmt” – which allows us to print “Hello, world!” in the console. Looks a bit like in Python, doesn’t it?
Variables, constants, and functions
Declaring variables works a little bit like in Java, except you don’t have to specify the variable’s type. Go should be able to figure it out. Also, variables aren’t segregated – you don’t have to use a single type of them in a given line of code. Look at the example below, where I’ve used three different kinds, one after the other.
1
2
3
4
5
6
7
8
|
package main
import “fmt”
func main() {
var i, j int = 1, 2
k := 3
c, python, java := true, false, “no!”
fmt.Println(i, j, k, c, python, java)
}
|
There are also constants in Go – you define them by inserting the word “const” before the variable, as seen in the following example.
1
2
3
4
5
6
7
8
9
10
|
package main
import “fmt”
const Pi = 3.14
func main() {
const World = “世界“
fmt.Println(“Hello”, World)
fmt.Println(“Happy”, Pi, “Day”)
const Truth = true
fmt.Println(“Go rules?”, Truth)
}
|
Functions also resemble what we know from Java – the difference is you have to use the “func” prefix before each one. You have to specify the name, the type of the transferred variable, as well as the type of the returned variable. Here, the difference between Go and Java is that in the case of the former there can be many returned values – it doesn’t have to be just one (e.g., an object).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package main
import “fmt”
func add(x int, y int) int (
return x + y
}
func main() {
fmt.Println(add(42, 13))
}
package main
import “fmt”
func swap(x, y string) (string, string) {
return y, x
}
func main () {
a, b := swap(“hello”, “world”)
fmt.Println(a, b)
}
|
Flow control
The “for” loop also looks a lot like in Java, the exception being some missing brackets in the place where you define the condition.
1
2
3
4
5
6
7
|
func main() {
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
fmt.Println(sum)
}
|
The same goes for the “if” loop, but in that case, you can also add a declaration and calculation of a variable based on which the condition should be checked.
1
2
3
4
5
6
|
func pow(x, n, lim float64) float 64 {
if v := math.Pow(x, n); v < lim {
return v
}
return lim
}
|
And finally the “switch” loop. There’s no “break” here – Go always checks the type it wants and that type is displayed. If there’s no “break”, it doesn’t check all of them.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
func main() {
fmt.Println(When’s Saturday?”)
today := time.Now().Weekday()
switch time.Saturday {
case today + 0:
fmt.Println(“Today.”)
case today + 1:
fmt.Println(“Tomorrow.”)
case today + 2:
fmt.Println(“In two days.”)
default:
fmt.Println(“Too far away.”)
}
}
|
Data structure
When it comes to the programming language’s data structure, in Go we have pointers, structs, arrays/slices, and maps.
Pointers work more or less like in C and C++ – they… well, they point, as the name suggests. But to be precise, they point to a place in memory, instead of a specific value, which can be helpful, because just like in Java, it’s the value, not the reference, that is transferred to the function (unless we transfer a pointer).
1
2
3
4
5
6
7
8
9
10
11
|
func main() {
i, j := 42, 2701
p := &i // point to i
fmt.Println(*p) // read i through the pointer
*p = 21 // set i through the pointer
fmt.Println(i) // see the new value of i
p = &j // point to j
*p = *p / 37 // divide j through the pointer
fmt.Println(j) // see the new value of j
}
|
Structs resemble a solution you can find in C++, and in Go they’re used to do similar things – the struct type aggregates any number of variables.
1
2
3
4
5
6
7
|
type Vertex struct {
X int
Y int
}
func main() {
fmt.Println(Vertex(1, 2))
}
|
In Go we have just a single type of a table, which always has to have a set value. Here’s what it looks like:
1
2
3
4
5
6
7
8
9
|
func main() {
var a [2]string
a[0] = “Hello”
a[1] = “World”
fmt.Println(a[0], a[1])
fmt.Println(a)
primes := [6]int{2, 3, 5, 7, 11, 13}
fmt.Println(primes)
}
|
There’s also something called a slice – the easiest way to look at it is to see it as a part of a bigger table. If you modify the slice, you’ll also affect the table it came from. The tool is often used as a dynamic table, because a slice can have a higher value than the default table. It looks like this:
1
2
3
4
5
|
func main() {
primes := [6]int(2, 3, 5, 7, 11, 13)
var s [] int = primes[1:4]
fmt.Println(s)
}
|
The last important element of Go’s data structure are the maps. They work like in Java – they have a key-value structure. The syntax is very simple:
1
2
3
4
5
6
7
8
9
10
11
|
type Vertex struct {
Lat, Long float64
}
var m = map[string]Vertex{
“Bell Labs” : {40.68433, –74.39967},
“Google”: {37.42202, –122.08408},
}
func main() {
fmt.Println(m)
}
|
Goroutines and Channels
The most interesting thing that you can find in Go are Goroutines and Channels.
Goroutines work in a way similar to koroutines you might know from Kotlin, which is to say they’re a way to create a very light thread. You can do that by adding the prefix “go” to the function you want to execute in a given thread.
1
2
3
4
5
6
7
8
9
10
|
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say(“world”)
say(“hello”)
}
|
Channels are used as an intermediary to transfer data between Goroutines. You can assign a certain value to the channel and you can then read it from it. This is one of the ways to synchronize Goroutines and threads.
1
2
3
4
5
6
7
8
9
10
11
|
func main() {
// make a new channel
messages := make(chan string)
go func() {
// write to channel
message <– “Hello World!”
}()
// read from channel
message := <–messages
fmt.Printf(“I got the message %s”, message)
}
|
Go programming language – examples of real-life use
I’ll now show you a couple of examples of using the Go programming language in my work.
Let’s start with transactions between databases. The important thing is that you have to ensure transactivity. Like I said before, Go is a pretty simple language, so you have to control everything yourself. You need to create a connection to the database, create context, start the transaction, execute whatever query you want, and then end the transaction with “Commit” or “Rollback”.
Another example – here’s how you can create a small REST API very fast (for example, a mock for a service you use and can’t access at the moment). In Go, it takes just a little while. You just need to add a library that’ll be responsible for HTTP communication. Using this library, you set up a server that’ll listen to port 8080, and if that port gets called, a certain message is displayed (depending on the type of request). There are also Mutex libraries that offer more control, but if you’re after something that can be done quickly and easily, that’s how you can achieve it:
Unit testing
You can write unit tests very quickly and easily. The only requirement the function testing your functionality needs to meet is being able to get a “testing” variable. What you need to know when it comes to Go and testing is that you need to check the condition yourself – there are no assert libraries that would do this for you.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package main
import “testing”
func TestSum(t *Testing.T) {
tables := [ ]struct {
x int
y int
n int
}{
{1, 1, 2},
{1, 2, 3},
{2, 2, 4},
{5, 2, 7},
}
for _, table := range tables {
total := Sum(table.x, table.y)
if total != table.n {
t.Errorf(“Sum of (%d+%d) was incorrect, got: %d, want: %d.”, table.x, table.y, total, table.n)
}
}
}
|
Here’s what would happen if – in the above function’s example – you swapped summation for multiplication:
Go vs Java – similarities and differences
Like many developers at Pretius, I work with Java on a daily basis, so when I started testing Go, it was natural for me to compare the two languages. The conclusions are interesting.
Go vs Java – what are the main similarities?
There aren’t that many, to be honest:
- The syntax is quite alike, as you’ve already seen.
- Like Java, Go also has a Garbage Collector function, which means you don’t have to clean memory and create memory fragments, like in C or C++.
- The language is also cross-platform and uses reflection – it’s very rare, but the functionality does exist.
Java and Go – biggest differences
Differences are a much broader subject:
- Go isn’t an OPP language – there’s no traditional polymorphing here, nor inheritance.
- There are also no classes, only structures.
- You can simulate some object patterns through the interface, but it’s problematic. For example, in Java’s case you can see whether a variable is private, public, or visible only for the packet – there’s nothing like that in Go. If a function or a variable is written with a capital letter, it’s visible outside the packet – that’s the only requirement when it comes to variables’ visibility or classes.
- Go is an imperative language, while Java is more of a declarative one. There’s no such thing as a dependency injection in Go – everything needs to be packaged and written just like it needs to happen. So, when you write in Go, you should use as little “magic” as you possibly can – when someone else reads the code, everything should be relatively clear for that person.
- In Java, a lot can be done using Spring and other frameworks, and here you usually do these things yourself. There are some frameworks for Go too, but they aren’t as popular or well-developed as in Java’s case. They also aren’t required to work with the language.
- Go is a multithreaded language, but the important thing here is that it has Goroutines. In case of standard multithreaded systems, you can only execute as many threads simultaneously, as your processor can handle. When you code in Go, you can have many, many more of these threads, because Goroutines are very light and can be executed by more than one processor thread at the same time. What’s more, they start up a lot faster and have built-in primitives to communicate with each other, as well as channels.
- Just like C++, Go has pointers which you can use to transfer something through the references.
- Duck typing – as the saying goes, “if something walks and talks like a duck, it’s a duck”. What does that have to do with Go? You don’t need to write specifically, that some class implements a given interface. In Go, if a structure uses some functions of an interface, then it also implements that interface. It’s that simple.
- Function as an argument – in Java you have Lambda, here it’s much simpler. Every function can be assigned to a variable.
- Exception handling – there’s none, and it’s both an advantage and a disadvantage of Go. As I’ve mentioned already, you have to do most things yourself. Even if you get an “err” (error) variable, Go won’t force you to do anything with it – you need to take care of that yourself.
- No JVM – Go is a lot lighter and faster than Java.
What is Go best for? Who uses it?
Go is a very good option when you work on following things:
- Distributed services;
- Web services;
- Microservices;
- Cloud native development (like AWS Lambda – very fast cold start);
- Creating tools (you can make them very quickly);
- Anything that uses intensive operations or streaming (a simple service that reads databases made in Go can be at least 10 times faster than the one created in Java; in some tests – like computing factorials – Go can be from 10 to even 100 times faster than Java).
Here are several examples of companies that already use Go:
- Google,
- Apple,
- Facebook,
- Docker,
- The New York Times,
- BBC,
- Monzo – a UK challenger bank that claims to use around 1600 microservices, all of them created in Go.
Here’s where my tutorial ends. I hope you learned something useful about the basic structure and usage of Go. And hopefully, thanks to my Go vs Java comparison, you now know whether Google’s programming language is something you might be interested in. If you need more information, check the following resources.
Go programming language – more information:
- Go’s website https://golang.org/
- Golang tutorial – https://tour.golang.org
- Golang playground – https://play.golang.org/
- Go’s GitHub repo – https://github.com/golang/go
Are you looking for a software development company?
Pretius has a great team of Java developers, and a lot of experience with using the technology in enterprise-grade systems. We also know our way around many different industries. Drop us a line at hello@pretius.com (or use the contact form below). We’ll get back to you in 48 hours and tell you what we can do for you.