Basics

This chapter gives a quick run-through of Go’s basic syntax, and the features that differentiate it from other languages. If you have never programmed in Go before, this chapter will give you the knowledge to start reading and writing simple Go programs. Even if you have programmed in Go before, we recommend that you still read this chapter. Through the examples, we will highlight common pitfalls in Go programs, and answer some questions that even experienced Go programmers might still have.

Program Structure

Go code is organized in packages containing one or more Go source files. When building an executable, we put our code into a package main with a single func main.

As mentioned in the Installation chapter, our Go code lives in GOPATH, which we’re saying is $HOME/go. Let’s say we want to write our first Go command, which randomly selects an item from a list and outputs it to stdout. We need to create a directory in our GOPATH, with a main.go file containing a package main and single main function:

$ mkdir -p $GOPATH/src/github.com/prodgopher/dinner
$ cd $GOPATH/src/github.com/prodgopher/dinner

In this directory we’ll add our main.go with the following code:

Random dinner selection
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"math/rand"
 6 	"time"
 7 )
 8 
 9 func main() {
10 	dinners := []string{
11 		"tacos",
12 		"pizza",
13 		"ramen",
14 	}
15 	rand.Seed(time.Now().Unix())
16 	fmt.Printf("We'll have %s for dinner tonight!\n", dinners[rand.Intn(len(dinn\
17 ers))])
18 }

We can run our code with go run main.go. The other option is to build our code as an executable and run that. To do that, we first run go build to make sure that the code compiles. Then we run go install:

$ go build
$ go install
$ cd $GOPATH/bin
$ ./dinner
We'll have pizza for dinner tonight!

We can also run these commands from outside of our GOPATH, like so:

$ go build github.com/prodgopher/dinner
$ go install github.com/prodgopher/dinner

If we want to expose this functionality in a package so we can reuse it in other places, we need to add this functionality to a package. Let’s say we want to return the name of the dinner, rather than the whole string, like “pizza”, “ramen”, etc. For convenience, let’s reuse the same github.com/prodgopher/dinner directory. Remove the main.go file and create a new dinner.go file that looks like this:

Random dinner selection package
 1 package dinner
 2 
 3 import (
 4 	"math/rand"
 5 	"time"
 6 )
 7 
 8 func Choose() string {
 9 	dinners := []string{
10 		"tacos",
11 		"pizza",
12 		"ramen",
13 	}
14 	rand.Seed(time.Now().Unix())
15 
16 	return dinners[rand.Intn(len(dinners))]
17 }

Now, somewhere outside of our dinner package directory (let’s use our home folder), we’ll invoke our new functionality in a file called main.go:

Using our random dinner selection package
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 
 6 	"github.com/prodgopher/dinner"
 7 )
 8 
 9 func main() {
10 	fmt.Println(dinner.Choose())
11 }
$ go run main.go
tacos

Now we have a convenient package for randomly selecting what to eat for dinner.

Variables and Constants

There are multiple ways to declare variables in Go. The first way, declaring a var with a given type, can be done like so:

var x int

With this type of declaration, the variable will default to the type’s zero value, in this case 0.

Another way to declare a variable is like this:

var x = 1

Similar to the above method, but in this case we can declare the specific contents. The type is also implied.

Lastly, the short-hand variable declaration:

x := 1

This is probably the most common way, and the type is also implied like the above. Sometimes the var declaration method is used stylistically to indicate that the variable will be changed soon after the declaration. For example:

1 var found bool
2 for _, x := range entries {
3     if x.Name == "Gopher" {
4         found = true
5     }
6 }

One key difference between var and := declarations is that the shorthand version (:=) cannot be used outside of a function, only var can. This means that variables in the global scope must be declared using var. This is valid:

Example using var to declare variable in global scope
1 package main
2 
3 import "fmt"
4 
5 var a = 1
6 
7 func main() {
8 	fmt.Println(a)
9 }

but this will not compile:

It is invalid to use shorthand variable declaration in the global scope
1 package main
2 
3 import "fmt"
4 
5 a := 1 // this is invalid, use var instead
6 
7 func main() {
8 	fmt.Println(a)
9 }

Running the above, we get the following error:

$ go run var_bad.go 
# command-line-arguments
./var_bad.go:5: syntax error: non-declaration statement outside function body

The other subtle difference between var-declarations and shorthand-declarations occur when declaring multiple variables. The following code is valid,

1 var a, b = 0, 1 // declare some variables
2 b, c := 1, 2    // this is okay, because c is new
3 fmt.Println(a, b, c) // Outputs: 0, 1, 2

but this is not valid:

1 var a, b = 0, 1 // declare some variables
2 var b, c = 1, 2 // this is not okay, because b already exists
3 fmt.Println(a, b, c)

The second example, when placed into a program, fails to compile:

./var_shorthand_diff2.go:7: b redeclared in this block
    previous declaration at ./var_shorthand_diff2.go:6

This is because the shorthand := may redeclare a variable if at least one of the variables to its left is new. The var declaration may not redeclare an existing variable.

Basic Data Types

Basic Types

Go supports the following basic types:

  • bool
  • string
  • int8, int16, int32, int64, int
  • uint8, uint16, uint32, uint64, uint
  • float32, float64
  • complex64, complex128
  • byte (alias for uint8)
  • rune (alias for int32)
Booleans

The bool type represents a boolean and is either true or false.

Usage of booleans in Go
 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	a, b := true, false
 7 	c := a && b
 8 	d := a || b
 9 
10 	fmt.Println("a:", a)
11 	fmt.Println("b:", b)
12 	fmt.Println("c:", c)
13 	fmt.Println("d:", d)
14 
15 	// Output: a: true
16 	// b: false
17 	// c: false
18 	// d: true
19 }

In the above example we first create a and b, and assign them the values true and false, respectively. c is assigned the value of the expression a && b. The && operator returns true when both a and b are true, so in this case c is false. The || operator returns true when either a or b are true, or both. We assign d the value of a || b, which evaluates to true.

Note that unlike some other languages, Go does not define true or false values for data types other than bool.

Strings

The string type represents a collection of characters. When defined in code, a string is a piece of text surrounded by double quotes ("). Let’s write a simple program using strings.

Usage of booleans in Go
1 package main
2 
3 import "fmt"
4 
5 func main() {
6 	sound := "meow"
7 	sentence := "The cat says " + sound + "."
8 	fmt.Println(sentence)
9 }

The example demonstrates that strings support the + operator for concatenation. The variable sentence contains a concatenation of the strings "The cat says ", “meow”, and ”.”. When we print it to the screen, we get The cat says meow.`

This only scratches the surface of strings in Go. We will discuss strings in more depth in the chapter on Strings.

Integers

The integer types can be divided into two classes, signed and unsigned.

Signed integers

The signed integer types are int8, int16, int32, int64, and int. Being signed, these types store both negative and positive values, but up to a maximum half the value of its uint counterpart. int8 uses 8 bits to store values between -128 and 127 (inclusive). int16 stores values in the range -32,768 to 32,767. int32 stores values in the range -2,147,483,648 to 2,147,483,647. int64 stores values in the range -263 to 263-1, which is to say, between -9,223,372,036,854,775,808 and 9,223,372,036,854,775,807.

Unlike the other signed integer types, the int type does not explicitly state its width. This is because it acts as an alias for either int32 or int64, depending on the architecture being compiled to. This means that it will perform optimally on either architecture, and it is the most commonly used integer type in Go code.

Go does not allow implicit type conversions. When converting between integer types, an explicit type cast is required. For example, see the following code:

Type mixing that will result in a compile-time error
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var i32 int32 = 100
 9 	var i64 int64 = 100
10 
11 	// This will result in a compile-time error:
12 	fmt.Println(i32 + i64)
13 }

This results in a compile-time error:

1 $ go run type_mix.go
2 type_mix.go:12:18: invalid operation: i32 + i64 (mismatched types int32 and i\
3 nt64)

To fix the error, we can either use the same types from the start, or do a type cast. We will discuss type casts again later in this chapter, but here is how we might use a type cast to solve the problem:

Using an integer type cast
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var i32 int32 = 100
 9 	var i64 int64 = 100
10 
11 	fmt.Println(int64(i32) + i64)
12 	// Output: 200
13 }

A nice feature of the Go compiler is that it warns us if a constant value overflows the integer type it is being assigned to:

Putting an overflowing constant into an integer type
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var i int8 = 128
 9 	fmt.Println(i)
10 }
$ go run int.go
int.go:8:15: constant 128 overflows int8

Here we try to assign a number that is one bigger than the maximum value of int8, and the compiler prevents us from doing so. This is neat, but as we will see in the next section on Unsigned integers, the compiler will not save us from calculations which result in over- or underflow.

Unsigned integers

Under unsigned integers, we have uint8, uint16, uint32, uint64, and uint. uint8 uses 8 bits to store a non-negative integer in the inclusive range 0 to 255. That is, between 0 and 28-1. Similarly, uint16 uses 16 bits and stores values in the inclusive range 0 to 65,535, uint32 uses 32 bits to store values from 0 to 4,294,967,295, and uint64 uses 64 bits to store values from 0 to 264-1. The uint type is an alias for either uint32 or uint64, depending on whether the code is being compiled for a 32-bit architecture or a 64-bit architecture.

uints are useful when the values to be stored are always positive. However, take special care before deciding to use uint. Go strictly enforces types, so uint requires a cast to be used with int. Built-in slice functions, like len, cap, and almost all library functions, return int. So using those functions with uint will require explicit type casts, which can be both inefficient and hard to read. Furthermore, underflow is a common enough problem with the uint type that it’s worth showing an example of how it can happen:

An example of uint underflow
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var p, s uint32 = 0, 1
 9 	fmt.Printf("p - s = %d - %d = %d", p, s, p-s)
10 	// Output: p - s = 0 - 1 = 4294967295
11 }

Running this, we get:

$ go run uint.go
p - s = 0 - 1 = 4294967295

As this example illustrates, if we are not careful when subtracting from the uint type, we can run into underflow and get a large positive value instead of -1. Be aware of the limitations before choosing unsigned integer types.

Floating point numbers

For floating point numbers we have two types, float32 and float64. A float32 represents a 32-bit floating-point number as described in the IEEE-754 standard, and float64 represents a 64-bit floating-point number.

An integer can be converted to a floating-point number with a type conversion:

1 x := 15
2 y := float64(x)

This will be especially useful when using the math package, as the functions of that package typically work with float64 (for example, math.Mod and math.Abs both take float64).

Complex numbers

Complex numbers are expressed by the types complex64 and complex128. A complex64 number is a float32 with real and imaginary parts, and a complex128 is a float64 with real and imaginary parts. Creating a complex number is done like so:

1 x := complex(1.0, 2.0)
2 fmt.Println(x)
1 (1+2i)

There are built-in functions to extract the real and imaginary parts of a complex number:

1 x := complex(1.0, 2.0)
2 fmt.Println(real(x))
3 fmt.Println(imag(x))
1 1
2 2

You can express an imaginary literal by appending i to a decimal of float literal:

1 fmt.Println(1.3i)
1 (0+1.3i)

Structs

A struct is a collection of fields, which can be declared with the type keyword:

Struct example
 1 package main
 2 
 3 import "fmt"
 4 
 5 type Person struct {
 6 	Name  string
 7 	Email string
 8 }
 9 
10 func main() {
11 	p := Person{"Robert", "robert@example.com"}
12 
13 	fmt.Println(p.Name)
14 }

Struct fields are accessed with a dot, so our above example should print:

Robert

Since structs are commonly used for reading and writing data formats such as JSON, there are struct tags which define how you would like your fields to be decoded or encoded in that data format. Here is an example of JSON struct tags:

Struct tags example
 1 package main
 2 
 3 import (
 4 	"encoding/json"
 5 	"fmt"
 6 	"log"
 7 )
 8 
 9 type Person struct {
10 	Name  string `json:"name"`
11 	Email string `json:"email"`
12 }
13 
14 func main() {
15 	p := Person{"Robert", "robert@example.com"}
16 	b, err := json.Marshal(p)
17 	if err != nil {
18 		log.Fatal(err)
19 	}
20 
21 	fmt.Println(string(b))
22 }

Running this code will output:

{"name":"Robert","email":"robert@example.com"}

If we didn’t have the struct tags, then we would have:

{"Name":"Robert","Email":"robert@example.com"}

since it isn’t very common to see the first letter of a field capitalized like that in JSON, we use the struct tags to define how we want our struct fields to be encoded.

One important note about struct field names and JSON: field names must be exported (first letter of field name must be capitalized) in order for encoding to JSON to work. If our struct field names were name and email, we would get an empty JSON object when marshalling.

Golang also supports anonymous structs, which can be commonly found in table-driven tests for example. You can see some examples in our Testing chapter, but here is a quick (not testing-related) example to show how it works:

Anonymous struct example
 1 package main
 2 
 3 import (
 4 	"encoding/json"
 5 	"fmt"
 6 	"log"
 7 )
 8 
 9 func main() {
10 	p := struct {
11 		Name  string `json:"name"`
12 		Email string `json:"email"`
13 	}{
14 		Name:  "Robert",
15 		Email: "robert@example.com",
16 	}
17 	b, err := json.Marshal(p)
18 	if err != nil {
19 		log.Fatal(err)
20 	}
21 
22 	fmt.Println(string(b))
23 }

This will print the same as our “Struct tags example” example above.

Operators

Operators in Go are very similar to other languages in the C-family. They can be divided into five broad categories: arithmetic operators, comparison operators, logical operators, address operators and the receive operator.

Arithmetic Operators

Arithmetic operators apply to numeric values. From the Go specification:

Arithmetic operators apply to numeric values and yield a result of the same type as the first operand. The four standard arithmetic operators (+, -, *, /) apply to integer, floating-point, and complex types; + also applies to strings. The bitwise logical and shift operators apply to integers only.

The following table summarizes the different arithmetic operators and when they may be applied:

+    sum                    ints, floats, complex values, strings
-    difference             ints, floats, complex values
*    product                ints, floats, complex values
/    quotient               ints, floats, complex values
%    remainder              ints

&    bitwise AND            ints
|    bitwise OR             ints
^    bitwise XOR            ints
&^   bit clear (AND NOT)    ints

<<   left shift             int << uint
>>   right shift            int >> uint

Comparison Operators

Comparison operators compare two operands and yield a boolean value. The comparison operators are:

==    equal
!=    not equal
<     less
<=    less or equal
>     greater
>=    greater or equal

In any comparison, the first operand must be assignable to the type of the second operand, or vice versa. Go is strict about types, and it is invalid to use a comparison operator on two types that are not comparable. For example, this is valid:

var x int = 1
var y int = 2

// eq will be false
var eq bool = x == y

but this is not valid, and will result in a compile-time type error:

var x int = 1
var y int32 = 2

// error: mismatched types int and int32
var eq bool = x == y

Logical Operators

Logical operators apply to boolean values and yield a boolean result.

&&    conditional AND    p && q  is  "if p then q else false"
||    conditional OR     p || q  is  "if p then true else q"
!     NOT                !p      is  "not p"

As in C, Java, and JavaScript, the right operand of && and || is evaluated conditionally.

Address Operators

Go has two address operators: the address operation, &, and the pointer indirection, *.

&x returns a pointer to x. The pointer will be a pointer of the same type as x. A run-time panic will occur if the address operation is applied to an x that is not addressable.

1 var x int
2 var y *int = &x
3 
4 // Print the memory address of x, 
5 // e.g. 0x10410020
6 fmt.Println(y)

When x is a pointer, *x denotes the variable pointed to by x. If x is nil, an attempt to evaluate *x will cause a run-time panic.

1 var x *int = nil
2 *x   // causes a run-time panic

The Receive Operator

The receive operator, <- is a special operator used to receive data from a channel. For more details on this, see channels.

Conditional Statements

We’ve seen some simple if statements in previous sections’ code snippets. Here we’ll cover some other common uses of conditional statements in Go.

An if can contain a variable declaration before moving on to the condition. This can often be seen in tests, like in this example from the Go source code (bytes/reader_test.go):

1 if got := string(buf); got != want {
2     t.Errorf("ReadAll: got %q, want %q", got, want)
3 }

Variables declared in the condition are restricted to the scope of the if statement - meaning that in the example above, we cannot access the got variable outside of the if statement’s scope.

An else statement is done as follows (this example is taken from Go’s time package, in time/format.go):

1 if hour >= 12 { 
2     b = append(b, "PM"...)
3 } else {
4     b = append(b, "AM"...)
5 } 

It is also quite common to see switch statements used in lieu of if/else statements. Here is an example from Go’s source code (net/url/url.go), of a switch statement:

 1 func ishex(c byte) bool {
 2     switch {
 3     case '0' <= c && c <= '9': 
 4         return true 
 5     case 'a' <= c && c <= 'f': 
 6         return true 
 7     case 'A' <= c && c <= 'F': 
 8         return true 
 9     }    
10     return false
11 }

This switch statement has no condition, meaning it is functionally equivalent to switch true.

A switch with a condition looks like this (taken from Go’s fmt/print.go):

1 func (p *pp) fmtBool(v bool, verb rune) {
2     switch verb {
3     case 't', 'v': 
4         p.fmt.fmt_boolean(v)
5     default:
6         p.badVerb(verb)
7     }    
8 }

and we can also declare a variable in the condition and switch on that:

1 switch err := err.(type) {
2     case NotFoundError:
3         ...
4 }

Arrays

An array of a specific length can be declared like so:

1 var x [3]int

In Go, however, arrays are rarely used unless you have a specific need for them. Slices are more common, which we’ll cover in the next section.

Slices

While arrays have a fixed size, slices are dynamic. To create a slice of integers for example, we can do:

1 x := []int{1, 2, 3, 4, 5}

Slices are abstractions on top of arrays. A slice contains a pointer to an array, its length, and its capacity. We can get the length and capacity of a slice with the built-in len and cap functions, respectively. We’ll call “slicing” the act of creating a new slice which points to a range of contiguous elements in the original array. We can “slice” arrays as well as slices - in which case the new slice will point to the underlying array. For example:

1 x := []int{1, 2, 3, 4, 5}
2 y := x[0:3]
3 fmt.Println(y)

will give us:

[1 2 3]

We can also leave out the 0:

1 y := x[:3]

and the result will be the same. Likewise for the high bound, we can leave that out and it will default to the length of the slice:

1 x := []int{1, 2, 3, 4, 5}
2 y := x[3:]
3 fmt.Println(y)

and we get:

[4 5]

A slice’s zero value is nil:

1 var x []int
2 fmt.Println(x == nil)
true

To append to a slice, we use the builtin append function:

1 x := []int{}
2 x = append(x, 1)
3 x = append(x, 2, 3)
4 y := []int{4, 5}
5 x = append(x, y...)
6 fmt.Println(x)

Notice the y... on line 5: append is a variadic function. The first argument is a slice, and the second is 0 or more arguments of the same type as the slice’s values. Running the above code will give us the following output:

[1 2 3 4 5]

Use copy to copy the contents of a slice:

1 x := []int{1, 2, 3}
2 y := make([]int, 3)
3 copy(y, x)
4 fmt.Println(x, y)
[1 2 3] [1 2 3]

Note that we’re using make to create the slice y, with a size argument of 3. This is to ensure that y has enough capacity to copy x into it. If we had used an empty y with 0 capacity, for example, our y would have remained empty:

1 x := []int{1, 2, 3}
2 y := []int{}
3 fmt.Println("y capacity:", cap(y))
4 copy(y, x)
5 fmt.Println(x, y)
y capacity: 0
[1 2 3] []

We can sort a slice with the sort.Slice function. All we have to do is provide the slice and a less argument which serves as a comparator function in which we define how one element in the slice is considered “less” than another when sorting:

Sorting slices
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"sort"
 6 )
 7 
 8 type Country struct {
 9 	Name       string
10 	Population int
11 }
12 
13 func main() {
14 	c := []Country{
15 		{"South Africa", 55910000},
16 		{"United States", 323100000},
17 		{"Japan", 127000000},
18 		{"England", 53010000},
19 	}
20 
21 	sort.Slice(c,
22 		func(i, j int) bool { return c[i].Name < c[j].Name })
23 	fmt.Println("Countries by name:", c)
24 
25 	sort.Slice(c,
26 		func(i, j int) bool { return c[i].Population < c[j].Population })
27 	fmt.Println("Countries by population:", c)
28 }

Running this, our output should be:

Countries by name: [{England 53010000} {Japan 127000000} {South Africa 559100\
00} {United States 323100000}]
Countries by population: [{England 53010000} {South Africa 55910000} {Japan 1\
27000000} {United States 323100000}]

Maps

Maps are a necessary and versatile data type in any programming language, including Go. Here we’ll go over some ways to use maps, and cover some idiosyncrasies in their usage.

First, as we know from earlier, there are a couple of ways to instantiate variables in Go, and this goes for maps as well. Let’s look at some of them:

1 var m = map[string]string{}
2 m := make(map[string]string)
3 m := map[string]string{}

The var declaration could be used in the top-level (or “package block”) scope:

1 package main
2 
3 var m = map[string]string{}
4 
5 func main() {
6 }

But otherwise these are all basically functionally equivalent.

If you’re familiar with maps in other programming languages, you can probably pick up on using maps in Go pretty quickly. Here is an example that is self-explanatory:

Using maps in Go
 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	m := make(map[string]string)
 7 	m["en"] = "Hello"
 8 	m["ja"] = "こんにちは"
 9 	// loop over keys and values of map
10 	for k, v := range m {
11 		fmt.Printf("%q => %q\n", k, v)
12 	}
13 
14 	// nonexistent key returns zero value
15 	fmt.Printf("zh: %q\n", m["zh"])
16 
17 	// check for existence with _, ok := m[Key]
18 	if _, ok := m["ja"]; ok {
19 		fmt.Printf("key %q exists in map\n", "ja")
20 	}
21 
22 	delete(m, "en")
23 	fmt.Printf("m length: %d\n", len(m))
24 	fmt.Println(m)
25 }

Running this code, we should get this output:

"en" => "Hello"
"ja" => "こんにちは"
zh: ""
key "ja" exists in map
m length: 1
map[ja:こんにちは]

The first output is from the loop, then we check a nonexistent key “zh”, then check for the existence of “ja”, print the length of the map, then print the map itself.

Another important note is that the map type is not safe for concurrent use. If you need to use a map in a concurrent way, take a look at sync.Map.

Also, the iteration order of maps is not guaranteed, so you can’t rely on any specific order when looping over your map.

Lastly, the following will make a nil map, which will panic when writing to it:

var m map[string]string

So avoid using this declaration style when making maps.

For further reading, although it is slightly outdated as it doesn’t mention sync.Map, check Go maps in action on the Go blog.

Loops

Loops in Go are done with the for construct. There is no while in Go, but you can achieve the same effect with for:

For loop
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	x := 1
 9 	for x < 4 {
10 		fmt.Println(x)
11 		x++
12 	}
13 }

The above code outputs:

1
2
3

A more traditional version that you may be familiar with is also available:

For loop with three components
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	for i := 0; i < 3; i++ {
 9 		fmt.Println(i)
10 	}
11 }

This will output:

0
1
2

An infinite loop can be expressed with an empty for:

Infinite for loop
1 package main
2 
3 func main() {
4 	for {
5 	}
6 }

To loop over a slice, we can do the following, where i is the index and x is the value at that index:

For loop over slice with index
 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	nums := []int{1, 2, 3}
 7 	for i, x := range nums {
 8 		fmt.Printf("nums[%d] => %d\n", i, x)
 9 	}
10 }

The above code will output:

nums[0] => 1
nums[1] => 2
nums[2] => 3

If we don’t need the index, we can leave it out with _:

For loop over slice without index
 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	nums := []int{1, 2, 3}
 7 	for _, x := range nums {
 8 		fmt.Println(x)
 9 	}
10 }

and this will output:

1
2
3

We can also range over the keys and values of a map like so:

Range over map
 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	m := map[int]string{
 7 		1: "一",
 8 		2: "二",
 9 		3: "三",
10 	}
11 	for k, v := range m {
12 		fmt.Printf("%d => %q\n", k, v)
13 	}
14 }

This will output:

1 => "一"
2 => "二"
3 => "三"

Functions

Functions are declared with the func keyword. They can take zero or more arguments, and can return multiple results.

Functions are first-class citizens, and can be stored in variables or used as values to other functions.

Exported Names

In a Go package, a name is exported if it starts with an uppercase letter. Take the following code for example:

Exported names
 1 package countries
 2 
 3 import (
 4 	"math/rand"
 5 	"time"
 6 )
 7 
 8 type Country struct {
 9 	Name       string
10 	Population int
11 }
12 
13 var data = []Country{
14 	{"South Africa", 55910000},
15 	{"United States", 323100000},
16 	{"Japan", 127000000},
17 	{"England", 53010000},
18 }
19 
20 // Random returns a random country
21 func Random() Country {
22 	rand.Seed(time.Now().Unix())
23 
24 	return data[rand.Intn(len(data))]
25 }

When importing this package, you would be able to access the countries.Country type, as well as countries.Random() function, but you cannot access the countries.data variable because it begins with a lowercase letter.

Pointers

Declaring a variable with * before the type indicates a pointer:

var p *int

The zero-value of this is nil. To generate a pointer to an existing variable, use &:

x := 100
p = &x

Now we can dereference the pointer with *:

fmt.Println(*p)

and this results in:

100

Lastly, there is no pointer arithmetic in Go. The authors decided to leave it out for reasons such as safety, and simplifying the implementation of the garbage collector.1

Goroutines

Goroutines are functions that run concurrently in a Go program. They are lightweight, and cheap to use. Prefixing a function with the go keyword will run the function in a new goroutine:

Using a goroutine
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"time"
 6 )
 7 
 8 func hello() {
 9 	fmt.Println("hello from a goroutine!")
10 }
11 
12 func main() {
13 	go hello()
14 	time.Sleep(1 * time.Second)
15 }

(Note that we have a call to time.Sleep in the main function. This is to prevent the main from returning before our goroutine completes. We’re using a sleep to show a small example of a goroutine; it is not a valid way of managing goroutines.)

It’s also common to see a goroutine used with an anonymous function:

1 go func() {
2     fmt.Println("hello from a goroutine!")
3 }()

One way to synchronize goroutines is to use sync.WaitGroup:

Using sync.WaitGroup
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"sync"
 6 )
 7 
 8 func main() {
 9 	var wg sync.WaitGroup
10 	wg.Add(3)
11 	go func() {
12 		defer wg.Done()
13 		fmt.Println("goroutine 1")
14 	}()
15 	go func() {
16 		defer wg.Done()
17 		fmt.Println("goroutine 2")
18 	}()
19 	go func() {
20 		defer wg.Done()
21 		fmt.Println("goroutine 3")
22 	}()
23 
24 	wg.Wait()
25 }
$ go run waitgroup_example.go
goroutine 3
goroutine 1
goroutine 2

In this example, we instantiate a sync.WaitGroup and add 1 to it for each goroutine. The goroutines then call defer wg.Done() to signify that they’ve finished. We then wait for the goroutines with wg.Wait().

When using sync.WaitGroup, we must know the exact number of goroutines ahead of time. If we had called wg.Add(2) instead of wg.Add(3), then we would risk returning before all of the goroutines finished. If we had called wg.Add(4), the code would have panicked with the following error:

fatal error: all goroutines are asleep - deadlock!

Another way to manage goroutines is with channels, which we’ll discuss in the next section.

Channels

Channels are used for sending and receiving values of a specific type. It is common to see them used inside of goroutines. Channels must be created with their specific type before use:

1 ch := make(chan int)

We can create a buffered channel like so:

1 ch := make(chan int, 5)

This means that sending to the channel will block when the buffer is full. When the buffer is empty, receives will block.

To send to a channel, we use the <- operator:

1 ch <- 5

And to receive a value from a channel, we do:

1 v := <-ch

Let’s see what happens when we send too many integers to a buffered channel of ints:

Sending too many ints to buffered channel
 1 package main
 2 
 3 func main() {
 4 	ch := make(chan int, 5)
 5 
 6 	ch <- 1
 7 	ch <- 2
 8 	ch <- 3
 9 	ch <- 4
10 	ch <- 5
11 	ch <- 6
12 }
$ go run channels_sending_too_many.go
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
	/Users/gopher/mygo/src/github.com/gopher/basics/channels_sending_too_many.go\
:11 +0xdb
exit status 2

We get an error - all goroutines are asleep - deadlock!. What if we were to read one int off of the channel before sending the final 6?

$ go run channels_read_one.go 
1

We’re no longer overfilling the buffered channel, because we read one int off of it before sending a sixth item.

What if we want to know whether a buffered channel is full before sending to it? There are a couple of ways we can do this.

One way would be to check the len of the channel before sending to it again:

Checking length of channel
 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	ch := make(chan int, 5)
 7 
 8 	ch <- 1
 9 	ch <- 2
10 	ch <- 3
11 	ch <- 4
12 	ch <- 5
13 
14 	fmt.Println("channel length:", len(ch))
15 	switch {
16 	case len(ch) >= 5:
17 		<-ch
18 	case len(ch) < 5:
19 		ch <- 6
20 	}
21 }
$ go run channels_check_length.go
channel length: 5

We could also use a select statement with a default that does nothing when the channel is full:

Channel select statement
 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	ch := make(chan int, 5)
 7 
 8 	ch <- 1
 9 	ch <- 2
10 	ch <- 3
11 	ch <- 4
12 	ch <- 5
13 
14 	select {
15 	case ch <- 6:
16 	default:
17 		fmt.Println("channel is full, ignoring send")
18 	}
19 }
$ go run channels_select.go 
channel is full, ignoring send

Interfaces

An interface in Go is a set of methods that another type can define in order to implement that interface. We define an interface type with the interface keyword:

1 type Entry interface {
2     Title() string
3 }

Now any concrete type we define that implements a Title() string method will implement the Entry interface:

Interfaces example
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"time"
 6 )
 7 
 8 type Entry interface {
 9 	Title() string
10 }
11 
12 type Book struct {
13 	Name      string
14 	Author    string
15 	Published time.Time
16 }
17 
18 func (b Book) Title() string {
19 	return fmt.Sprintf("%s by %s (%s)", b.Name, b.Author, b.Published.Format("Ja\
20 n 2006"))
21 }
22 
23 type Movie struct {
24 	Name     string
25 	Director string
26 	Year     int
27 }
28 
29 func (m Movie) Title() string {
30 	return fmt.Sprintf("%s (%d)", m.Name, m.Year)
31 }
32 
33 func Display(e Entry) string {
34 	return e.Title()
35 }
36 
37 func main() {
38 	b := Book{Name: "John Adams", Author: "David McCullough", Published: time.Da\
39 te(2001, time.May, 22, 0, 0, 0, 0, time.UTC)}
40 	m := Movie{Name: "The Godfather", Director: "Francis Ford Coppola", Year: 19\
41 72}
42 	fmt.Println(Display(b))
43 	fmt.Println(Display(m))
44 }

Note that the Display function takes e Entry, not a concrete type like Book or Movie. Our concrete types implement the Entry interface, so we’re now allowed to pass implementations of those types into any function that takes an Entry.

The empty interface

We define an empty interface as interface{}, and it can hold a value of any type:

1 var i interface{}
2 i = "こんにちは"
3 fmt.Println(i)
1 こんにちは

If we want to test whether an interface is a certain type, we use a type assertion:

1 var i interface{}
2 i = "こんにちは" 
3 s, ok := i.(string)
4 if !ok {
5     fmt.Println("s is not type string")
6 }
7 fmt.Println(s)
1 こんにちは

In our example above, i is a type string, so the second return value from our type assertion is true, and s contains the underlying value. If i had been another type, such as an int, then our ok would have been false and our s would have been the zero value of the type we were trying to assert, or in other words 0.

Nil interfaces

An interface in Go is essentially a tuple containing the underlying type and value. For an interface to be considered nil, both the type and value must be nil. Here is an example:

Nil interfaces example
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var i interface{}
 9 	fmt.Println(i == nil)
10 	fmt.Printf("%T, %v\n", i, i)
11 
12 	var s *string
13 	fmt.Println("s == nil:", s == nil)
14 
15 	i = s
16 	fmt.Println("i == nil:", i == nil)
17 	fmt.Printf("%T, %v\n", i, i)
18 
19 }
true
<nil>, <nil>
s == nil: true
i == nil: false
*string, <nil>

Note how our s variable is nil, but when we set our interface i to s then check if i is nil, i is not considered nil. This is because our interface has an underlying concrete type, and interfaces are only nil when both the concrete type and the value are nil.

Error Handling

Functions in Go often return an error value as the final return argument. When the function does not encounter any error conditions, we return nil. The error type is a builtin interface type that we can create with functions like errors.New and fmt.Errorf. As an example, let’s make a function that parses a string and returns the boolean value that string represents. This function is inspired by the ParseBool function in the Go standard library’s strconv package:

Error handling example
 1 package strconv
 2 
 3 import "fmt"
 4 
 5 func ParseBool(str string) (bool, error) {
 6 	switch str {
 7 	case "1", "t", "T", "true", "TRUE", "True":
 8 		return true, nil
 9 	case "0", "f", "F", "false", "FALSE", "False":
10 		return false, nil
11 	}
12 	return false, fmt.Errorf("invalid input %q", str)
13 }

Here we are returning a nil error when we’re able to parse the input properly, and using fmt.Errorf to return an error in the case that we cannot parse the input.

We’ll cover more about error handling in the “Style and Error Handling” chapter that follows this one.

Reading Input

You can use a bufio.Scanner to read input from stdin, which by default will split the input line by line. Here is an example of a Go program that reads a file containing one word per line, and keeps a count of every occurrence of each word:

Reading standard input
 1 package main
 2 
 3 import (
 4 	"bufio"
 5 	"log"
 6 	"os"
 7 )
 8 
 9 func main() {
10 	m := map[string]int{}
11 	scanner := bufio.NewScanner(os.Stdin)
12 	for scanner.Scan() {
13 		m[scanner.Text()]++
14 	}
15 	if err := scanner.Err(); err != nil {
16 		log.Printf("ERROR: could not read stdin: %s", err)
17 	}
18 	for k, v := range m {
19 		log.Printf("%s => %d", k, v)
20 	}
21 }

If we had a file that looked like this:

go
python
ruby
php
go
go
go

and we piped it into our program like so:

$ go run main.go < langs.txt

we would see output like the following (order is not guaranteed when iterating over maps, so the order of our output might change when running more than once):

2017/10/13 13:12:21 go => 4
2017/10/13 13:12:21 python => 1
2017/10/13 13:12:21 ruby => 1
2017/10/13 13:12:21 php => 1

Writing Output

One way of writing output to a file in Go is to use the *File returned from os.Create. os.Create will create a file for reading and writing. If the file already exists, it will be truncated:

Writing output
 1 package main
 2 
 3 import (
 4 	"log"
 5 	"os"
 6 )
 7 
 8 func main() {
 9 	langs := []string{"python", "php", "go"}
10 	f, err := os.Create("langs.txt")
11 	if err != nil {
12 		log.Fatal(err)
13 	}
14 	defer f.Close()
15 	for _, lang := range langs {
16 		f.WriteString(lang + "\n")
17 	}
18 }

Running this code will give us the following content in a file called langs.txt:

python
php
go

Another utility function we could use is ioutil.WriteFile which will open the file and write our data in one function call.