Go (Programming Language) Reference

Kip Landergren

(Updated: )

My cheat sheet for the Go programming language covering installation, basic commands, and resources.

Contents

Tooling

The go Tool

go bug start a bug report
go build compile packages and dependencies
go clean remove object files and cached files
go doc show documentation for package or symbol
go env print Go environment information
go fix update packages to use new APIs
go fmt gofmt (reformat) package sources
go generate generate Go files by processing source
go get add dependencies to current module and install them
go install compile and install packages and dependencies
go list list packages or modules
go mod module maintenance
go run compile and run Go program
go test test packages
go tool run specified go tool
go version print Go version
go vet report likely mistakes in packages

go build

-C dir change to dir before running the command
-o output force build to write the resulting executable or object to the named output file or directory. If the named output is an existing directory or ends with a slash or backslash, then any resulting executables will be written to that directory.
go build -o ./bin foo.go

go env

Print Go environment information:

go env

go help

go help
go help [command]
go help [command] [subcommand]

gopls

Installation

go install golang.org/x/tools/gopls@latest

Configuration

Review official settings documentation.

Variables

GOPATH defaults to $HOME/go on Unix
GOBIN defaults to $GOPATH/bin
GOMODCACHE defaults to $GOPATH/pkg/mod

GOPATH is used by:

Note: GOPATH was previously used for “GOPATH development mode” or “GOPATH mode” which was superceded by Go modules.

Writing Software in Go

Language Idioms

Zero Values

When creating a new type, construct it such that its zero value is useable without further setup.

comma-ok

iterator.More()

Table Testing

See “Testing” section.

Packages

Structure and naming conventions:

More information available from:

Personal Conventions

Process

Testing

Test all packages in a module, recursively:

go test -v ./...

Table testing example:

package main

import (
	"testing"
)

func Test_NewInstructions(t *testing.T) {
	// put our test cases into a "table" where the map key represents the name
	// and the struct{} represents the test case
	testCases := map[string]struct {
		line        string
		instruction Instruction
	}{
		"y direction": {
			line: "fold along y=7",
			ins: Instruction{
				Raw:   "fold along y=7",
				Dir:   "up",
				Value: 7,
			},
		},
		"x direction": {
			line: "fold along x=5",
			ins: Instruction{
				Raw:   "fold along x=5",
				Dir:   "left",
				Value: 5,
			},
		},
	}

	// run each of the test cases, in parallel, as subtests
	for name, testCase := range testCases {
		tc := testCase // capture for scope

		// sub test `tSub` will be run in its own goroutine
		t.Run(name, func(tSub *testing.T) {
			tSub.Parallel()

			i, err := NewInstruction(tc.line)
			if err != nil {
				tSub.Log("how did this happen?") // prefer to fmt.Println()
				tSub.Fatal(err)
			}

			if i.Raw != tc.ins.Raw {
				tSub.Fatalf("expected Raw: %q got: %q\n", tc.ins.Raw, i.Raw)
			}

			if i.Dir != tc.ins.Dir {
				tSub.Fatalf("expected Dir: %q got: %q\n", tc.ins.Dir, i.Dir)
			}

			if i.Value != tc.ins.Value {
				tSub.Fatalf("expected Value: %d got: %d\n", tc.ins.Value, i.Value)
			}

		})

	}

}

WebAssembly

Notes:

GOOS=js GOARCH=wasm go build -o ./main.wasm

Go Terminology

fmt.Fprint, and others
file (“F”) print; golang uses io.Reader
fmt.Sprint, and others
string (“S”) print
build
compiles packages, along with their dependencies, but does not install the results
channel
typed conduit for sending and receiving values
closure
a function value that references variables from outside its body
default format
how a value’s type is printed
format verb
the specifier, like %v and %T, used to print operands of functions like fmt.Sprintf
function
takes zero or more arguments
goroutine
a lightweight thread managed by the go runtime
import path
a string used to import a package. A package's import path is its module path joined with its subdirectory within the module
install
the compilation of code to create a binary executable, and then copying that executable into a directory
interface
a set of method signatures; can be thought of as a tuple containing a concrete value and a type
method
a function with a receiver argument
module
a collection of related Go packages that are released together
module path
the import path prefix for all packages within the module
package
a collection of source files in the same directory that are compiled together
pointer
a pointer holds the memory address of a value
pointer type / reference type
a type that contains a pointer to the concrete type
precision
for floats, the number of places after the decimal
slice capacity
the number of elements in the underlying array, counting from the first item in the slice
slice length
the number of elements it contains
width
for most values this is the minimum number of runes to output, padding if necessary. refer to the fmt package documentation for special cases and more info.

Frequently Asked Questions (FAQ)

What is the difference between a nil slice and an empty slice?

A nil slice has no pointer to an underlying array. An empty slice has a pointer to an (empty) underlying array. Both nil and empty slices have a len and cap of 0.

Further explanation in answer: The zero value of a slice is not nil

Does a map resize down after deletions?

No, there is ongoing discussion in the GitHub issue “runtime: shrink map as elements are deleted”

Can you initialize a map with a default value?

No built-in way. Instead check presence and set value when absent.

What is the size of Go’s int? What is the width of Go’s int?

Architecture dependent:

What does the “A” stand for in strconv.Atoi() (and strconv.Itoa())?

ASCII

Does Go have a ternary operator?

No, use an if else conditional. See Why does Go not have the ?: operator?.

How to print my object as a string?

Implement the Stringer interface.

Does Go have a Set data structure?

No, not built-in and not in the standard library. Sets can be emulated using maps or via third party libraries.

Resources

Note: lists below are not exhaustive.

Installation

Documentation

Libraries

go-study

README.md

# go-study

personal study area for the Go programming language

lang/constants.go

package lang

// allowed constant types
const One = 1         // numbers
const A = 'A'         // runes
const Hello = "HELLO" // strings
const False = false   // bools

// not allowed constant types; must be `var`
var m map[string]int // maps. no const immutable maps
var a [5]int         // arrays
var s []int          // slices

lang/statements/iteration.go

package statements

import "fmt"

func iterationDemo() {
	// for loop with single condition, form 1 (AKA while loop)
	sum := 0
	for sum < 100 {
		sum += 2
	}

	// for loop with single condition, form 2 (if condition is absent,
	// it is equivalent to `true`)
	someCondition := true
	for {
		if someCondition {
			someCondition = false
			fmt.Println("continuing")
			continue
		} else {
			fmt.Println("breaking out")
			break
		}
	}

	// for loop with `for` clause / normal for loop
	for i := 0; i < 10; i++ {
		sum += i
	}

	// for loop with `range` clause

	// example: ranging over a slice / range over a slice
	pow := []int{1, 2, 4, 8, 16, 32, 64, 128}
	for i, v := range pow { // i is index; v is _copy_ of element at i
		fmt.Printf("2**%d = %d\n", i, v)
	}

	// example: ranging over a string / range over string. note: the
	// index i may not be what you expect—range moves over Unicode
	// code points (runes) and the index is the index in the string's
	// underlying array of bytes.
	//
	// this prints:
	//
	// 0: 你
	// 3: 好
	// 6: 世
	// 9: 界
	for i, rune := range "你好世界" {
		fmt.Printf("%d: %c\n", i, rune)
	}

	// example: ranging over a map / range over map. note: order is not guaranteed!
	m := map[string]int{
		"foo": 0,
		"bar": 1,
	}
	for key, value := range m {
		fmt.Printf("%s: %d\n", key, value)
	}

	// example: ranging over a channel / range over channel
	c := make(chan int)
	f := func(c chan int) {
		for _, v := range []int{10, 20, 30} {
			c <- v
		}
		close(c)
	}

	go f(c)

	for v := range c {
		fmt.Printf("%d\n", v)
	}

	// for loop emulating do-while loop
	var counter int
	for ok := true; ok; ok = (counter < 1) {
		fmt.Println("we win only once!")
		counter++
	}

}

lang/statements/switch.go

package statements

import "fmt"

func switchDemo() {
	input := "Hello World!"

	score := 0
	for _, r := range input {

		switch r {
		case 'a', 'A':
			fallthrough // required to fall through
		case 'e', 'E':
			fallthrough
		case 'i', 'I':
			fallthrough
		case 'o', 'O':
			fallthrough
		case 'u', 'U':
			score += 10
			// does not fall through
		case '!':
			score += 10
			// does not fall through
		default:
			score -= 1
		}
	}

	fmt.Println(score)
}

lang/types/basic_types.go

package types

// bool zero value. boolean zero value
var b bool // false

// uninitialized string. string zero value
var str string // ""

// int zero value
var i int // 0

var i8 int8

var i16 int16

var i32 int32

var i64 int64

var ui uint

var ui8 uint8

var ui16 uint16

var ui32 uint32

var ui64 uint64

var uiptr uintptr

var by byte // alias for uint8

var r rune // alias for uint32; represents unicode code point

var f32 float32

var f64 float64

var c64 complex64

var c128 complex128

lang/types/map.go

package types

import "fmt"

func mapDemo() {
	// uninitialized map. map zero value. an uninitialized map cannot be added to
	var m map[string]int // nil. key type is string, value type is int

	// map literal. map init. empty map
	m = map[string]int{}

	// make map. map init
	m = make(map[string]int)

	// make map with initial space for approximately 5 elements. map init
	m = make(map[string]int, 5)

	// map set key value. map insert. map update
	m["1"] = 1
	m["2"] = 2
	m["3"] = 3
	m["4"] = 4
	m["5"] = 5
	m["6"] = 6 // map automatically grows to accommodate

	// map delete key
	delete(m, "6")

	// map get value. if not present, will be value type's zero value
	maybeElem := m["6"]

	// map check if key exists. maybeElem will still be the value type's zero
	// value if not found
	maybeElem, ok := m["6"]
	if ok {
		// key was found in our map
	}

	// map check if key exists
	_, ok = m["6"]
	if ok {
		// key was found in our map
	}

	// combined syntax
	if maybeElem, ok = m["6"]; ok {
		// key was found in our map
	} else {
		// key was not found in our map
	}

	// common syntax. val only exists in scope of the conditional
	if val, ok := m["6"]; ok {
		// do something with val
		fmt.Println(val)
	} else {
		// do something having not found it
	}

	// common syntax. you need to use val
	val, ok := m["6"]
	if !ok {
		// set val to a default
		val = -1
	}

	// map size. map length. size of map
	if 0 < len(m) {
		// take some action
	}

	// map copy. copy map. map copy keys and values. alternatively: use `maps.Copy`
	m2 := map[string]int{}
	for k, v := range m {
		m2[k] = v
	}

	fmt.Println(m, maybeElem, val, m2)
}

lang/types/slice.go

package types

import "fmt"

func sliceDemo() {
	// uninitialized slice. slice zero value
	var s []int // nil

	// the zero value can have elements appended. slice append. append slice.
	s = append(s, 0) // append 0 to s, assigning it to s

	// slice literal. slice init. empty slice
	s = []int{}

	// make slice with initial size 10. slice init
	s = make([]int, 10)

	// slice append. slice concat
	s1 := []int{0, 1, 2}
	s2 := []int{3, 4, 5}
	s3 := append(s1, s2...) // append the contents of s2 to s1
	fmt.Println(s3)         // [0 1 2 3 4 5]

	// slice get first value
	first := s3[0]

	// slice get last value
	last := s3[len(s3)-1]

	// slice remove first value. slice remove first element.
	s3 = s3[1:]

	// slice remove last value. slice remove last element
	s3 = s3[:len(s3)-1]

	fmt.Println(first, last, s3)

	// slice fill with -1
	s = make([]int, 10)
	for i := range s {
		s[i] = -1
	}
}

lang/types/string.go

package types

import "fmt"

func stringDemo() {
	// string literal. string init
	str := "hello world"

	// multiline string. multi-line string. multi line string
	str = `use
backticks
for a multi-line string`

	str = "or use\nnewline characters\n"

	// cannot escape backticks
	str = `no way
to escape backticks,
but you can ` + `do
this`

	fmt.Println(str)
}

lang/variables/swap.go

package variables

func Swap() {
	// swap two variables
	a, b := 0, 10
	b, a = a, b
}

stdlib/errors_demo/errors.go

package errors_demo

import (
	"errors"
	"fmt"
	"os"
)

func errorsDemo() {
	// create new error. make new error. make error. create error.
	err := errors.New("I am an error!")

	// check error type
	if errors.Is(err, os.ErrClosed) {
		fmt.Println("huh?")
	}
}

stdlib/fmt_demo/fmt.go

package fmt_demo

import (
	"fmt"
	"os"
)

func fmtDemo() {
	// fmt.Sprint: short for "string print"

	// spaces added automatically for consecutive non-strings. manual spaces
	// required for strings.

	// concatenate strings
	// concat strings
	// string concat
	// append strings
	// string append
	s := fmt.Sprint(3, 2, 1, " hello", " world!\n")
	fmt.Printf(s)

	s = fmt.Sprintf("format verbs work as expected: %d + %d = %d\n", 3, 5, 3+5)
	fmt.Printf(s)

	// spaces and trailing newline automatically added for Sprintln
	s = fmt.Sprintln("hello", "world!")
	fmt.Printf(s)

	// fmt.Print: short for "print"

	// convention: no error check

	// spaces added automatically for consecutive non-strings. manual spaces
	// required for strings.
	fmt.Print(3, 2, 1, " hello", " world!\n")
	fmt.Printf("format verbs work as expected: %d + %d = %d\n", 3, 5, 3+5)
	// spaces and trailing newline automatically added for Println
	fmt.Println("hello", "world!")

	// fmt.Fprint: short for "file print"

	// spaces added automatically for consecutive non-strings. manual spaces
	// required for strings.
	fmt.Fprint(os.Stdout, 2, 1, " hello", " world!\n")
	fmt.Fprintf(os.Stdout, "format verbs work as expected: %d + %d = %d\n", 3, 5, 3+5)
	// spaces and trailing newline automatically added for Fprintln
	fmt.Fprintln(os.Stdout, "hello", "world!")

	// double quotes
	doubleQ := `"we win!"`
	s = fmt.Sprintf("%s", doubleQ)
	fmt.Println(s) // "we win!"
	s = fmt.Sprintf("%q", doubleQ)
	fmt.Println(s) // "\"we win!\""

	// gotcha: the number of runes to express the number includes the `.` character

	// width is the minimum number of runes to output. float width, floating
	// point width, printing floats.
	val := 123.456
	s = fmt.Sprintf("%f", val)
	fmt.Println(s) // 123.456000
	s = fmt.Sprintf("%1f", val)
	fmt.Println(s) // 123.456000
	s = fmt.Sprintf("%2f", val)
	fmt.Println(s) // 123.456000
	s = fmt.Sprintf("%3f", val)
	fmt.Println(s) // 123.456000
	s = fmt.Sprintf("%4f", val)
	fmt.Println(s) // 123.456000
	s = fmt.Sprintf("%5f", val)
	fmt.Println(s) // 123.456000
	s = fmt.Sprintf("%6f", val)
	fmt.Println(s) // 123.456000
	s = fmt.Sprintf("%7f", val)
	fmt.Println(s) // 123.456000
	s = fmt.Sprintf("%8f", val)
	fmt.Println(s) // 123.456000

	// precision is the amount of runes after the decimal place. float precision,
	// floating point precision. printing floats.
	s = fmt.Sprintf("%f", val)
	fmt.Println(s) // 123.456000
	s = fmt.Sprintf("%.f", val)
	fmt.Println(s) // 123
	s = fmt.Sprintf("%.1f", val)
	fmt.Println(s) // 123.5
	s = fmt.Sprintf("%.2f", val)
	fmt.Println(s) // 123.46
	s = fmt.Sprintf("%.3f", val)
	fmt.Println(s) // 123.456
	s = fmt.Sprintf("%.4f", val)
	fmt.Println(s) // 123.4560
	s = fmt.Sprintf("%.5f", val)
	fmt.Println(s) // 123.45600
	s = fmt.Sprintf("%.6f", val)
	fmt.Println(s) // 123.456000
	s = fmt.Sprintf("%.7f", val)
	fmt.Println(s) // 123.4560000
	s = fmt.Sprintf("%.8f", val)
	fmt.Println(s) // 123.45600000

	// width and precision can be combined, with any excess padded with spaces by
	// default
	s = fmt.Sprintf("%f", val)
	fmt.Println(s) // 123.456000
	s = fmt.Sprintf("%12.f", val)
	fmt.Println(s) //          123
	s = fmt.Sprintf("%12.1f", val)
	fmt.Println(s) //        123.5
	s = fmt.Sprintf("%12.2f", val)
	fmt.Println(s) //       123.46
	s = fmt.Sprintf("%12.3f", val)
	fmt.Println(s) //      123.456
	s = fmt.Sprintf("%12.4f", val)
	fmt.Println(s) //     123.4560
	s = fmt.Sprintf("%12.5f", val)
	fmt.Println(s) //    123.45600
	s = fmt.Sprintf("%12.6f", val)
	fmt.Println(s) //   123.456000
	s = fmt.Sprintf("%12.7f", val)
	fmt.Println(s) //  123.4560000
	s = fmt.Sprintf("%12.8f", val)
	fmt.Println(s) // 123.45600000

	// excess can also be zero padded. zero-padded. zero padding
	s = fmt.Sprintf("%08.2f", val)
	fmt.Println(s) // 00123.46

}

stdlib/fmt_demo/formatting_verbs.go

package fmt_demo

import "fmt"

func formattingVerbsDemo() {
	// formatting verbs of note
	v := struct{ x int }{x: 1}

	// literal percent sign.
	fmt.Printf("%%  \n") // %

	// value in default format.
	fmt.Printf("%v  \n", v) // {1}

	// value in default format, with field names
	fmt.Printf("%+v  \n", v) // {x:1}

	// value in Go-syntax
	fmt.Printf("%#v  \n", v) // struct { x int }{x:1}

	// the uninterpreted bytes of a string
	fmt.Printf("%s  \n", "foo") // foo

	// boolean: the word `true` or `false`
	fmt.Printf("%t  \n", true) // true

	// a double-quoted, and safely escaped, string
	fmt.Printf("%q  \n", `"foo"`) // "\"foo\""
}

stdlib/log_demo/log.go

package log_demo

import "log"

func logDemo() {
	log.Printf("hello %s\n", "world")

	log.Println("hello world")
}

stdlib/os_demo/os.go

package os_demo

import (
	"errors"
	"fmt"
	"log"
	"os"
)

func osDemo() {
	// create a file. write to a file. does nothing if it exists due to `os.O_EXCL`
	f, err := os.OpenFile("path/to/file.md", os.O_RDWR|os.O_CREATE|os.O_EXCL, 0755)
	defer f.Close()
	if err != nil {
		if errors.Is(err, os.ErrExist) {
			log.Printf("could not create file. skipping. message: %v\n", err)
		} else {
			log.Fatalf("could not create file. message: %v\n", err)
		}
	} else {
		fmt.Fprintln(f, "some text!")
	}

}

stdlib/sort_demo/sort.go

package sort_demo

import (
	"sort"
)

func sortDemo() {
	fruit := []string{"durian", "canteloupe", "apple", "banana"}

	// sort strings in place. sort string slice
	sort.Strings(fruit)                              // ascending
	sort.Sort(sort.Reverse(sort.StringSlice(fruit))) // descending

	// note: sort.StringSlice attaches the methods of Interface to []string,
	// sorting in increasing order.

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

	// sort ints in place. sort int slice
	sort.Ints(counts)                              // ascending
	sort.Sort(sort.Reverse(sort.IntSlice(counts))) // descending

	// note: `sort.IntSlice` attaches the methods of Interface to []int, sorting
	// in increasing order. `sort.Reverse` just tweaks that to call `Less(i, j)`
	// as `Less(j, i)`

	// sort structs in place. sort struct slice
	type coord struct {
		x int
		y int
	}
	coords := []coord{{3, 2}, {1, 2}, {1, 1}}
	sort.Slice(coords, func(a int, b int) bool {
		if coords[a].y == coords[b].y {
			return coords[a].x < coords[b].x
		}

		return coords[a].y < coords[b].y
	})
}

stdlib/strconv_demo/strconv.go

package strconv_demo

import (
	"fmt"
	"log"
	"strconv"
)

func main() {
	// string convert ascii to integer. convert string to integer.
	i, err := strconv.Atoi("1")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(i) // 1
}

stdlib/strings_demo/strings.go

package strings_demo

import (
	"log"
	"strings"
)

// use this package when manipulating UTF-8 strings
func stringsDemo() {
	// split string on whitespace
	fruits1 := strings.Fields("apple banana cantaloupe")
	log.Println(fruits1)

	// split string on delimiter
	fruits2 := strings.Split("apple,banana,cantaloupe", ",")
	log.Println(fruits2)
}

stdlib/unicode_demo/unicode.go

package unicode_demo

import (
	"unicode"
)

func unicodeDemo() {
	// report bool if rune is lower case
	unicode.IsLower('r')

	// report bool if rune is upper case
	unicode.IsUpper('r')
}

tricks/bit_shifting_tricks.go

package tricks

// bit shifting: shift the binary representation of `1` `20` bits to the left. equivalent to 1 * 2^20
const OneMB = 1 << 20 // 1 MB

tricks/math_tricks.go

package tricks

import (
	"fmt"
	"math"
)

func demoMathTricks() {
	a := 10
	b := 3

	// integer floor. int floor
	floor := a / b

	// integer ceiling. int ceiling
	ceil := int(math.Ceil(float64(a) / float64(b)))

	fmt.Println(floor, ceil)
}

tricks/slice_tricks.go

package tricks

// make a range from low to high, 0 to num
func makeRange(low, high int) []int {
	var r []int

	for i := low; i < high+1; i++ {
		r = append(r, i)
	}

	return r
}

// get minimum of slice. minimum from slice. no built-in
func getMin(input []int) int {
	var min int

	// note: be aware of how this behaves with the empty slice
	for i, v := range input {
		if i == 0 || v < min {
			min = v
		}
	}

	return min
}

x/exp/maps/maps.go

package maps

import (
	"fmt"

	"golang.org/x/exp/maps"
)

func demoMaps() {
	m1 := make(map[string]*int)
	count := 0
	m1["count"] = &count

	// map clone. this is a shallow copy. note the behavior when we change a
	// value that is a reference type
	m2 := maps.Clone(m1)

	*m1["count"]++

	fmt.Println(m1)
	fmt.Println(m2)

	// map copy. copy map.
	maps.Copy(m2, m1)

	*m1["count"]++
	fmt.Println(m1)
	fmt.Println(m2)
}

x/exp/slices/slices.go

package slices

import "golang.org/x/exp/slices"

func demoSlices() {
	items := []string{"a", "b", "c"}

	// slice contains. slice includes
	slices.Contains(items, "a") // true
	slices.Contains(items, "d") // false
}