Tap the power of Google's Go language

Learn the key concepts behind programming in Go, a concise, simple, safe, and fast compiled language with powerful concurrency features

Tap the power of Google's Go language
Picthai.com (CC0)

Google's Go, aka Golang, is an open source programming language that makes it easy to build simple, reliable, and efficient software. It’s part of the programming language lineage that started with Tony Hoare’s Communicating Sequential Processes, and it includes Occam, Erlang, Newsqueak, and Limbo.

While somewhat C-like, Go follows in the lineage of languages such as Erlang that implement lightweight concurrency; thus, Go is really good for systems that need massive scale. Go implements functional programming in an elegant manner, within a strongly typed, garbage-collected language. And though relatively young, Go has been battle-tested on enormous projects.

In the following guide, we’ll demonstrate some of the differentiating features of the language and its tools, including its extremely lightweight concurrency. The project currently has more than 500 contributors, led by Rob Pike, a distinguished engineer at Google, who worked at Bell Labs as a member of the Unix team and co-created Plan 9 and Inferno.

Gophers, unite!

Install Go

To install the Go language compiler and tools, first browse to golang.org and check the version at the bottom of the page. Then check your installed Go version from a command line:

$ go version
go version go1.8 darwin/amd64

If Go is not found or the official version is newer, then click on the Download Go link on the Golang home page, click again to download the featured binary download for your system, and follow the installation instructions.

Another alternative for Go language and tools installation is to use the ActiveState distribution.

go download IDG

Configure your Go environment

Go programmers typically keep all their code in one workspace, with bin, pkg, and src folders. Within each folder, the projects typically have paths that relate to Git repositories. For example, I keep all my Go language code under ~/work, and set my GOPATH environment variable to $HOME/work. The path to my "hello.go" source code folder is $GOPATH/src/github.com/meheller/hello.

I also add the GOPATH/bin directory to my path, for convenience in running Go programs from any directory:

export GOPATH=$HOME/work
export PATH=$PATH:$(go env GOPATH)/bin

go configure lg IDG

The Go language utilities will install in GOPATH by default, so putting the GOPATH/bin directory on the path also makes it easier for you and Go-aware editors and IDEs to find them. You can install most of the utilities with $go get <repo-path> once you know which ones you need and their repository paths. The repositories are usually easy to find with a Google search. In some cases, an editor plugin for Go will install the utilities (such as gocode) automatically.

If you don't set it yourself, GOPATH defaults to $HOME/go on Unix and MacOS and %USERPROFILE%/go on Windows.

Use a Go-aware editor or IDE

As I discussed in "The best Go language IDEs and editors," a Go-aware editing and development environment can boost your productivity significantly. In that article I recommended Gogland as a desktop IDE, Visual Studio Code with the vscode-go plugin (shown below) as a desktop editor, and Cloud9 as a cloud IDE.

go ide IDG

Slices

The Go language extends the idea of arrays with slices. A slice points to an array of values and includes a length. []T is a slice with elements of type T. In the exercise below, we use slices of slices of unsigned bytes to hold the pixels of an image we generate; package main is where programs start running. The import statement is an extended version of the include statement in C and C++; here we get the pic file from a Mercurial repository. The := syntax declares and initializes a variable, and the compiler infers a type whenever it can. Also, make is used to create slices and some other types. A for..range loop is the equivalent of the for..in loop on C#.

go slices lg IDG

Maps

The Go map statement maps keys to values. As with slice, you create a map with make, not new. In the example below, we are mapping string keys to integer values. Here we demonstrate inserting, updating, deleting, and testing for map elements. As we'll see in a later example, you can't depend on the order or iteration over maps.

The program below prints:

The value: 42 The value: 48 The value: 0 The value: 0 Present? false

go map lg IDG

Structs and methods

The Go language lacks classes but has a struct, which is a sequence of named elements, called fields, each of which has a name and a type. A method is a function with a receiver. A method declaration binds an identifier, the method name, to a method, and it associates the method with the receiver's base type. In this example we declare a Vertex struct to contain two floating point fields, X and Y, and a method Abs. Fields that begin with uppercase letters are public; fields that begin with lowercase letters are private. Fields and methods are addressable through the dot notation; * and & signify pointers, as in C. The program below prints 5.

go method lg IDG

Interfaces

An interface type is defined by a set of methods. A value of interface type can hold any value that implements those methods. In this example, we define an interface Abser and a variable a of type Abser. Note that the assignments in lines 17 and 18 work, but the assignment in line 22 does not even compile. The Abs method of Vertex, which we saw in the previous example, has a pointer to Vertex type for its receiver, so a *Vertex implements Abser, but a Vertex does not.

go interfaces lg IDG

Switch

The switch statement in Go is similar to the switch statement in other C-like languages, except that the case statements can be types or expressions in addition to simple values, and the cases automatically break unless they end with fallthrough statements. The cases are evaluated in the order they are defined.

go switch lg IDG

Goroutines

Goroutines are, to a rough approximation, extremely lightweight threads, in the spirit of Tony Hoare’s Communicating Sequential Processes. Line 16 in the sample below calls the say function asynchronously, while line 17 calls the say function synchronously. Goroutines, channels, and select statements form the core of Go’s highly scalable concurrency, one of the strongest selling points of the language. The language also has conventional synchronization objects, but they are rarely needed. The program below outputs:

hello world hello world hello world hello world hello

go goroutines lg IDG

Channels

Channels in Go provide a mechanism for concurrently executing functions to communicate by sending and receiving values of a specified element type. The value of an uninitialized channel is nil. In line 16 of the example below, we create a bidirectional channel of integers. We could also make unidirectional sending <-c and receiving c<- channels. In lines 17 and 18 we call sum asynchronously with slices of the first and second half of a. In line 19, the integer variables x and y receive the two sums from the channel. In line 7, the underscore _, the blank identifier, means to ignore the first result value from the for..range loop, which is the index. The program output is 17 -5 12.

go channels lg IDG

Range and close

A sender can close a channel to indicate that no more values will be sent. Receivers can test whether a channel has been closed by assigning a second parameter to the receive expression. A loop for i := range c receives values from the channel repeatedly until it is closed. The cap of the channel is the capacity, which is the size of the buffer in the channel, set as the optional second argument when you make a channel, as in line 17. Note the compact form of the assignment statements in the fibonacci function. The program output is the first 10 values of the Fibonacci series, 0 through 34.

go range close lg IDG

Select

A select statement chooses which of a set of possible send or receive operations will proceed. It looks similar to a switch statement but with all the cases referring to communication operations. A select blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple cases are ready.

Here the main function calls the fibonacci function with two unbuffered channels: one for results and one for a quit signal. The fibonacci function uses a select statement to wait on both channels. The anonymous, asynchronous go function that starts at line 21 waits to receive values at line 23, then prints them. After 10 values, it sets the quit channel so that the fibonacci function knows to stop.

go select lg IDG

Concurrency patterns, example 1

In this example, we use select to create a fan-in goroutine that combines two input channels of string, input1 and input2, into one unbuffered output channel, c. The select statement allows fanIn to listen to both input channels simultaneously and relay whichever is ready to the output channel. It doesn’t matter that both cases use the same temporary variable name to hold the string from its respective input channel. The example is from Rob Pike’s 2012 talk on Go Concurrency Patterns.

go concurrency1 Rob Pike

Concurrency patterns, example 2

The code below implements a parallel search of the internet, sort of like what Google actually does. To begin with, replicas …Search is a variadic parameter to the function; both Search and Result are types defined elsewhere.

The caller passes N search server functions to the First function, which creates a channel c for results and defines a function to query the ith server and saves it in searchReplica. Then First calls searchReplica asynchronously for all N servers, always returning the answer on channel c, and returns the first result to come back from the N servers. The example is from Rob Pike’s 2012 talk on Go Concurrency Patterns.

go concurrency2 Rob Pike

The net/http package

The Go net/http package provides HTTP client and server implementations. This example implements a simple web server that returns the contents of the directory /usr/share/doc to a web client.

go http lg IDG

The example does not work properly in the golang.org online environment, but run on a Mac command line it returns the following to a web browser asking for http://localhost:8080/:

bash/
ccid/
cups/
groff/
ntp/
postfix/

Package template

The Go html/template package implements data-driven templates for generating HTML output that is safe against code injection. Without all of the escaping added by the html/template package, the example could have produced a runnable JavaScript string, Hello, <script>alert('you have been pwned')</script>!.

go html template lg Google

Don't depend on order of iteration over maps

Maps, which we introduced earlier, use hash tables under the covers. To iterate over the entries in a map, you use the range keyword. While you might expect the iteration to occur either in sorted order or in the order of insertion, that simply isn't how hash tables work.

go map iteration order lg IDG

If you want the output to be in insertion order, then you need to keep track of the keys as they are inserted, typically in an array, then use the array range to drive the retrieval of the map elements. If you want the output in sorted order, then sort the array keys:

import "sort" 

var m map[int]string
var keys []int
for k := range m {
    keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
    fmt.Println("Key:", k, "Value:", m[k])
}

(The code above was shamelessly stolen from Nathan LeClaire, who in turn shamelessly stole it from Andrew Gerrand.)

Combine Go with code generators

The go build command is quick and efficient, but it isn't make or cmake: It only deals with go source files and is not a general-purpose build tool. While go generate still isn’t a general-purpose build tool, it does help you automate code generation from within go source files yet not resort to makefiles.

There are two pieces to this: the go generate command-line tool and special comment directives (see the top line in the example below) inside Go language source files that control generation. Together they can help reduce the amount of copying and pasting you have to do to create boilerplate code.

go generate lg Google

Rob Pike's original blog post on this feature (from which the example comes) initially talks about using Yacc for Go (now called goyacc), but not many of us actually write parser generator grammars. More often, we hoi polloi write simple templates. For example, later in the same blog post Pike talks about using stringer combined with go generate to automatically generate String methods for enumerated types. In another blog post, Kel Cecil describes using the Go text/template facility in a custom Go command-line app, combined with go generate, to automatically generate calls to the GamesDB web service.

What can you do with go generate?

Test in parallel

Most Go programmers know about the go test command and the Go testing package, but not all Gophers realize they can be used to perform fairly sophisticated actions without a lot of work. For example, there are easy ways to implement black box testing (from an external package), skip long-running tests (using the -short and/or -timeout flags), run benchmarks [func BenchmarkXxx(*testing.B)], and run tests and benchmarks in parallel [t.Parallel() and b.RunParallel].

The example below demonstrates benchmarking text templating in parallel. The environment variable GOMAXPROCS controls the number of parallel goroutines created.

go parallel testing IDG

Check your test coverage

How do you know how much of your code has been reached by your tests? You use a coverage tool while you run your tests.

Most coverage tools handle debugging-oriented tasks, such as adding break points to your binary and keeping track of them as they are hit. The go test -cover command works differently. It rewrites your source code to add GoCover.Count statements before compilation. For example:

func Size(a int) string {
    GoCover.Count[0] = 1
    switch {
    case a < 0:
        GoCover.Count[2] = 1
        return cnegative"

After the tests run, the tool collects the counters and computes the percentage covered by seeing how many were set. This is far more efficient at runtime (each added line compiles to one mov instruction) than using break points.

go cover lg Google

Call C, C++, Fortran, Objective C, and SWIG code—or not

Cgo is the feature that allows Go to call C, C++, Fortran, Objective C, and SWIG code. Whether that's a good idea depends on what you're calling and whether your Go program needs to be portable.

To trigger Cgo, you need only import "C" somewhere in your Go program, for example:

// #include <stdio.h>
// #include <errno.h>
import "C"

The Go code can then refer to types such as C.size_t, variables such as C.stdout, or functions such as C.putchar. The go build command will detect the import and expand the files it processes to build the Go application, treating .c, .s, and .S files as C source; .cc, .cpp, and .cxx files as C++ source; and .f, .F, .for, and .f90 files as Fortran source.

The temptation to use Cgo is strong when you have a large, working body of code that you'd prefer not to translate to Go. There are other factors to consider, however. Cross-compilation is problematic if you use Cgo: You need to find and maintain cross-compilation C toolchains for all your targets. The Go tools, such as testing, profiling, and coverage analysis, don't know about C code. You'll be introducing "thunks" when your code moves from Go to C and back. Finally, you'll lose the often-touted Go advantage of having a single, static binary.

1 2 Page 1
Page 1 of 2
InfoWorld Technology of the Year Awards 2023. Now open for entries!