Sergey Nivens - Fotolia
These Go code examples showcase its main features
Google's golang uses many of the same functions as older code languages, such as key-value pairs and compiling, but is better fitted to modern systems development requirements.
The Go programming language, aka golang, simplifies and streamlines tasks that programmers want to perform, but it also relies on some challenging concepts. Dive into some main features and Go code examples here to ascertain how it works and why users choose it.
Go was designed at Google to be simple, yet retain some of the functions of more complex languages. Go is suitable for systems programming, which covers infrastructure and OSes. Go challenges low-level, decades-old systems programming language C++ and also departs from the approach of common languages, such as Python. Container engine Docker and container orchestration tool Kubernetes are written in Go. It also is the language for Google App Engine infrastructure abstraction for Google Cloud Platform and Vitess to shard MySQL databases.
The main problem with C++ is the complexity of dealing with memory, as compared to languages such as Java, which rides atop a VM where it controls memory. C++ gives the programmer freedom to write to any memory address. When a programmer makes a mistake with C or C++, it can crash the OS and create bugs that hackers exploit. Memory addresses are referenced via pointers, a topic that confuses programmers. C and C++ programmers must specifically assign addresses to store objects. Go retains pointers, but it creates and destroys memory for the programmer and does not write to restricted areas of memory.
Features of Go
To write code with Go, get acquainted with some key Go features. Then, we'll look at these features in various Go code examples.
Compiled code. Code that is compiled is reduced to the ones and zeros of machine language, plus a small runtime to handle memory assignment, i.e., garbage collection. Theoretically, compiled code runs faster than interpreted languages, such as Java and Python. Java is quasi-compiled to bytecode but requires a VM to execute; Python requires a Python interpreter. The downside of compiled languages is the lack of a command-line tool to write code one line at a time. A command-line tool makes it easy to try ideas one line at a time. Instead, try an online compiler, such as The Go Playground.
Pointers and parameters by reference. The upside of addressing values by their memory address is the programmer can update values in place. With functions, this act is called passing values by reference as opposed to by value. When a language passes values by reference, it avoids copying a variable. The following generic -- not Go -- syntax illustrates the benefit.
In this function call, we pass x to the function simpleFunction. It returns a value that we assign to x again:
x = 0
x = simpleFunction(x)
simpleFunction(byval: x)
return 2 * x
This setup is redundant. The variable x in simpleFunction is not the same variable x declared just above. Instead, the computer creates a second copy.
When you call functions by reference, you can update the calling parameter directly:
x = 0
simpleFunction(byref: x)
simpleFunction(x)
2 * x
Therefore, simpleFunction knows that x is declared elsewhere. A call to simpleFunction(x) updates x directly. There's no need for a return statement.
Multithreaded programming. Programs that spin off threads create a complex task for programmers, who must write code to tell a program to wait while another thread runs. Two threads could update the same variable, and threads can become deadlocked. The golang concurrency model is designed to be type-safe, which prevents threading mistakes. In contrast to the amount of code a user writes in Java and Python to manage a thread, a Go programmer simply writes go someFunction().
Install Go and get going
A couple of examples will illustrate the Go language. First, install Go. On a Linux system with apt package management, use >sudo apt install golang-go.
To write Go code online, use https://golang.org/.
Author's note: This is not an interactive interpreter; it compiles the code, albeit on servers in the cloud.
Start with the simplest Go code example:
package main
import "fmt"
func main() {
fmt.Println("Hello World")
}
Then, compile the code, or compile and run it in one step:
go run samples.go
Hello World
All Go programs require a package name. Use import to import modules, such as the fmt module to format printed output. The rest of Go programming resembles C or Scala code with parentheses () and curly braces {}.
When you write Go code, declare all variables. This code produces the compile-time error undefined: x because x is not declared:
package main
import "fmt"
func main() {
x = 0
}
Declare x first with:
var x = 0
Or declare and initialize a variable in one step, as shown:
package main
import "fmt"
func main() {
x := 1
fmt.Println(x)
}
If statements in Go
The if statement in Go code looks the same as in many other languages, except that it does not require parentheses. Use == as a test of equality and not a single equals sign, which assigns a value. In the Go language example shown below, % (modulo, which is the remainder after division) tests whether x is even:
package main
import "fmt"
func main() {
x := 4
if x%2 == 0 {
fmt.Println(x, " is even")
}
}
Try putting the command before the item to check it in one line:
package main
import "fmt"
func main() {
if x := 4;x%2 == 0 {
fmt.Println(x, " is even")
}
}
Arrays for numbers
Go gives the programmer precise control over what type of numbers they declare. For example, instead of just int to designate 32 bits on a 32-bit CPU, Go programmers use an int32 (32-bit integer) or int64 (64-bit integer) statement. The Go code example below declares an empty array of two 32-bit floating point numbers. Then, the code assigns the value 3.41 to the second element and prints it:
package main
import "fmt"
func main() {
var a [2] float32
a[1] = 3.41
fmt.Println(a[1])
}
Maps
Maps are Key-value pairs for Go code. In the programming example below, the map command creates a map wherein the key is a string and values are ints.
package main
import "fmt"
func main() {
products := make(map[string]int)
products["bike"] = 1
products["trike"] = 2
fmt.Println(products)
}
Run this code with the commands here:
go run samples.go
map[bike:1 trike:2]
We could also declare the map and assign values in the same line:
products := map[string]int{"bike": 1, "trike": 2}
Variadic functions
A variadic function has a variable number of arguments. For example, fmt.Println is one. A variadic function can, for example, calculate the product of a set of numbers.
Use an ellipsis ... to mean a variable number of operators. Initialize the product to 1, and multiply each value by the next one using the unary operator *=.
Author's note: It's possible to write this particular line of code as product = product * num instead of product *= num, but that is longer.
Use the blank operator _ to ignore the index of the items in the array range nums. This example of variadic functions in Go looks like this:
package main
import "fmt"
func multiply(nums ... float32) float32 {
var product float32 = 1
for _, num := range nums {
product *= num
}
return product
}
func main() {
fmt.Println(multiply(1,2,3,4,5))
}
Run it with the code:
go run samples.go 12345
120
Get comfortable with pointers
Pointers are difficult for programmers to understand. One could read the classic book C Programming Language, written by mathematicians at AT&T, or follow this gentler approach to the topic.
Pointers are variables that point to a memory address. That memory address holds the value of a variable. Therefore, if p points to v, the value of p is the address in memory where the value of v is stored.
Briefly, the pointer operators in Go are:
*, which is the dereferencing operator and tells the program to provide the value of whatever this points to.
&, which gives the address of an item.
The Go code example below illustrates how pointers work. It declares p to be a pointer to an int. It has no value yet. Then, the line declares a variable v and assigns it the value 42. You can use the & operator to assign p to the address of v. Then, obtain the value of v from the dereferencing operator *.
package main
import "fmt"
func main() {
var p *int
v := 42
p = &v
fmt.Println("p points to v =", *p)
fmt.Println("v =", v)
fmt.Println("address of v =", p)
}
Note that *p gives the value of p. But just printing p gives the value of p, which is the address in memory where v is stored.
Running the program yields:
p points to i = 42
i = 42
address of i = 0xc420012050
Pointers are useful in part for the same reason as calling functions by reference instead of value. Read more about them and other topics in the golang documentation as you grow more familiar with Go for systems programming.