ruk·si

🐹 Go Guide

Updated at 2016-11-05 12:35

This guide contains tips how to write Go code.

Go aka. Golang is a compiled programming language with memory management. The main usage is for HTTP and TCP servers. Especially microservices as Docker container running a Go app image can take as little as 4 MB.

The language aims to keep balance between performance and ease of programming. Simply put, it's a mixture C and Python. Static typing and fast compile times.

Go is at it's best in distributed systems and tasks that can be divided to multiple simple operations as the goroutine concurrency model is really easy to use after you use it once or twice.

There aren't great dedicated IDEs for Go, but I'd recommend Visual Studio Code with Go Plugin for newbies.

Architecture

Go comes with a great set of command-line tools. Learn them well.

go build    # compile the program including its dependencies
go install  # compile and move executable to $GOPATH/bin for command-line usage
go get      # download, compile and install a program from github
go test     # run tests on the program
go run      # compile temporary executable and run it
gofmt       # format and simplify code
godoc       # generate and view documentation

I recommend also getting golint. Gofmt reformats Go source code, whereas golint prints out style mistakes.

# Run this after you have set your GOPATH
go get -u github.com/golang/lint/golint

Workspace is the main point of reference for Go. GOPATH environmental variable points to your workspace root. The best approach is to have a single GOPATH if your projects allow it.

# Example: GOPATH is /Projects/myproject_gopath
# There are some script files in the project folder to show what
# different commands would do in this setup.
GOPATH/
    bin/ # Programs that have the `main` function are compiled here.
        myproject.exe
    pkg/ # Programs that don't have the `main` function are compiled here.
        windows_amd64/github.com/ruksi/mydependency.a
    src/ # Project source codes are here, organized by import path
        github.com/
            ruksi/
                myproject/ # You can host this on Github.
                    .git/
                    main.go
                    main_test.go
                    README.md
                mydependency/
                    .git/
                    mydependency.go
                    mydependency_test.go
                    README.md
# Create a new worspace directory: .../myproject_gopath
mkdir myproject_gopath && cd myproject_gopath

# Set GOPATH to point to this directory.
# Windows: setx GOPATH %~dp0
# Unix: export GOPATH=$(pwd)

# Pull down the code, dependencies and puts them in your GOPATH/src.
go get -v -d github.com/ruksi/myproject
# If called without the -d, will also compile the packages to bin and pkg

# You can also just create ./src/github.com/ruksi/myproject/main.go file
# and start coding.

Add all GOPATH/bins into your PATH. Makes it possible to run binaries installed by go get.

Go supports all major version control systems. Native package system has integration with Git, Mercurial, Bazaar and Subversion. You can even download packages from services like GitHub.

import "github.com/ruksi/gosagne"

Every Go program is made up of packages. If you are writing an executable code (versus a library), then you need to define a main package and a main() function which will be the entry point. All running asynchronous processes are terminated when main function returns. Library packages don't need to have the main function.

package main

import "fmt"

func boring() {
    fmt.Println("boring!")
}

func main() {
    fmt.Println("start")
    go boring() // wont print anything as program exits before this is run
    fmt.Println("end")
}

All files run init function automatically. Each source file may have an init function that is ran after imports, reading of variables and types, but before the main call is made if the file has the main function.

package main

import "fmt"

func boring() {
    fmt.Println("boring!")
}

var greeting string
var farewell string

func init() {
    greeting = "HELLO WORLD!"
    farewell = "BYEBYE WORLD!"
}

func main() {
    fmt.Println(greeting)
    fmt.Println(farewell)
}

Make your code easy to use. If your repository is exclusively a binary or library, make sure consumers can go get or import it by its base path. That is, put the package main or primary importable resource in github.com/name/repo, and use subdirectories for helper packages.

If your code is primarily a binary, but can be used as a library:

github.com/ruksi/foo/
    main.go      // package main
    main_test.go // package main
    lib/
        foo.go      // package foo
        foo_test.go // package foo

If your code is primarily a library, but can be used as a binary:

github.com/ruksi/foo/
    foo.go      // package foo
    foo_test.go // package foo
    cmd/
        foo/
            main.go      // package main
            main_test.go // package main

Prefer go install over go build. If you produce a binary, check out getgb. GOOS + GOARCH + go build = cross platform builds.

Dependencies

Using other packages is done using import. Imports that you don't use can be renamed to _ while developing, but should never be saved to version control.

package main

import (
    "fmt"
    _ "time"
)

func main() {
    fmt.Println("Hello World!") // => Hello World!
}

Go has excellent standard libraries. It has everything you need to write web server or socket server. Read them through once in a while and use them as you need.

Dependency management can be confusing at first. Go doesn't have any kind of version control for imported dependencies e.g. information that you are using version >1.4.75 of ModuleNameX. The easiest way is to use an external Go dependency management library. Or you can manage them by yourself by having one workspace per project. It works fine as long as you do not have two dependencies that depend on different versions of a same library.

Consider default go vendoring for dependency management: Support came in 1.5 (with GO15VENDOREXPERIMENT=1 env) and default in 1.6. You can use a library for locking the dependencies e.g. glide for maximal control.

Libraries should never vendor their dependencies. Only main package should be allowed to have a vendor folder, libraries (no main package) shouldn't have a vendor folder. Libraries with vendored dependencies are very hard to use.

Make dependencies explicit. Testing hard-coded default and global libraries is hard. Use many small interfaces to model dependencies.

// bad
func foo() {
    resp, err := http.Get("http://example.com")
}

// good
type Doer interface {
    Do(*http.Request) (*http.Response, error)
}

func foo(d Doer) {
    req, _ := http.NewRequest("GET", "http://example.com", nil)
    resp, err := d.Do(req)
    // ...
}

Code Layout

Always run formatter on your code. Go has a standardized coding style. You may it customize with a few parameters. Keep the same coding style per project.

# Fix all style problems in Go files.
gofmt -w *.go

# Fix all style problems in Go files with some custom rules.
gofmt -w -tabs=false -tabwidth=4 *.go

# Also try to simplify code.
gofmt -w -tabs=false -tabwidth=4 -s *.go

Naming

Good names are consistent, short and accurate. The greater the distance between a name's declaration and its uses, the longer the name should be.

Almost all name identifiers are in camelCase. Packages, directories and file are in lowercase without any separators.

# exceptions:
# test files end in `_test`
main.go
main_test.go

Entity access level comes from first character's letter case. When a name starts with a lowercase letter, it is private and unaccessible outside the package. When a name starts with an uppercase letter, it is public and accessible outside the package.

func protectedFunction(t string) {}
func PublicFunction(t string) {}
var protectedVariable = 1
var PublicVariable = 1
type protectedType struct {}
type PublicType struct {}

Acronyms should be all capitals. E.g. ServeHTTP and IDProcessor.

Keep local variables short.

// bad
index
reader

// good
i
r

Keep parameters short.

// good
func AfterFunc(d Duration, f func()) *Timer
func Unix(sec, nsec int64) Time
func HasPrefix(s, prefix []byte) bool

Return values of exported functions should only be named for documentation.

// good
func Copy(dst Writer, src Reader) (written int64, err error)
func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error)

Receivers should be one or two characters. Receiver names should be consistent across all methods.

// good
func (b *Buffer) Read(p []byte) (n int, err error)
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request)
func (r Rectangle) Size() Point

Use *er naming for single-method interfaces.

type Reader interface {
    Read(p []byte) (n int, err error)
}

Package names should lend meaning to the names they export.

// bad
bytes.ByteBuffer
strings.StringReader

// good
bytes.Buffer
strings.Reader

Variables

Declaring variables without defining. You use var notation to declare variables. Variables are defined as the default or "zero" value of the type, e.g. 0 for integers.

package main

import "fmt"

func main() {
    // integer zero value is 0
    var myZeroInt int
    fmt.Println("myZeroInt:", myZeroInt) // => 0

    // string zero value is ''
    var myZeroStr string
    fmt.Println("myZeroStr:", myZeroStr) // => ''

    // boolean zero value is false
    var myZeroBool bool
    fmt.Println("myZeroBool:", myZeroBool) // => false
}

Make the zero value useful. The most common zero value, nil, can be a no-op.

func newFoo(..., cfg fooConfig) *foo {
    if cfg.Output == nil {
        cfg.Output = ioutil.Discard
    }
    // ...
}

Automatically defining variables is done with :=. There is var-notation and comma-notation. var-notation may also define the type. You can introduce multiple variables with a single var. Colon-notation can only be used inside functions.

package main

import "fmt"

// multiple file-wide variables
var (
    one = 1
    two = 2
    three int = 3
)

func main() {
    fmt.Println("Sum:", one + two + three) // => 6

    // var-notation declaration, then definition
    var myNumber
    myNumber = 2
    fmt.Println("myNumber:", myNumber) // => 2

    // var-notation declaration with type definition
    var yourNumber int = 1
    yourNumber = 2
    fmt.Println("yourNumber:", yourNumber) // => 2

    // colon-notation aka. short variable declaration
    ourNumber := 1
    ourNumber = 2
    fmt.Println("ourNumber:", ourNumber) // => 2
}

All defined variables must be used. Otherwise the program won't compile. Using _ as the variable name disables this.

package main

import "fmt"

func main() {
    //zero := 0 // would crash, unused variable
    _ = 0

    var one int
    _ = one

    two := 2
    fmt.Println(two)

    numbers := []int{10, 20, 30}
    for _, number := range numbers {
        fmt.Println(number)
    }
}

Watch out for unwanted variable shadowing. Can create hard to find bugs.

package main

import "fmt"

func main() {
    x := 1
    fmt.Println(x)     // => 1
    {
        fmt.Println(x) // => 1
        x := 2         // NOTE, A DECLARATION
        fmt.Println(x) // => 2
    }
    fmt.Println(x)     // => 1

    fmt.Println("")

    y := 1
    fmt.Println(y)     // => 1
    {
        fmt.Println(y) // => 1
        y = 2          // NOTE, AN ASSIGNMENT
        fmt.Println(y) // => 2
    }
    fmt.Println(y)     // => 2
}

const is used to define constant variables. const statement can appear anywhere var-statement can.

package main

import "fmt"

// file-wide constants
// note that these should he uppercase if you want to export them
const (
    one = 1
    two = 2
    three = 3
)

func main() {
    fmt.Println("Sum:", one + two + three) // => 6

    const myDemonicConstant = 666
    // myDemonicConstant = 999 // This would fail.
    fmt.Println("myDemonicConstant:", myDemonicConstant) // => 2
}

Enumerated values can be done with iota. iota is a predefined variable that holds an integer that is incremented every time it is read. The values of iota is reset when code encounters a const declaration.

package main

import "fmt"

const (              // iota is reset to 0
    freeEnum1 = iota // 0
    freeEnum2 = iota // 1
    freeEnum3 = iota // 2
)

type EnumType int
const (                         // iota is reset to 0
    typedEnum1 EnumType = iota  // 0
    typedEnum2                  // 1
    typedEnum3                  // 2
)
func (et EnumType) String() string {
    var str string
    if et == typedEnum1 {
        str = "Zero"
    } else if et == typedEnum2 {
        str = "One"
    } else if et == typedEnum3 {
        str = "Two"
    }
    return str
}

func main() {
    fmt.Println("freeEnum1:", freeEnum1) // => 0
    fmt.Println("freeEnum2:", freeEnum2) // => 1
    fmt.Println("freeEnum3:", freeEnum3) // => 0

    fmt.Println("typedEnum1:", typedEnum1) // => Zero
    fmt.Println("typedEnum2:", typedEnum2) // => One
    fmt.Println("typedEnum3:", typedEnum3) // => Two
}

Data Types

Snippets related to specific data types:

// Integer range depends on the environment.
// You can expect it to be at least uint8.
var number uint = 7;

// Byte is between 0 - 255, alias for uint8.
var x byte = 255

// Floating point number, 64-bit, a.k.a. float64.
floatNumber := 3.14195

// Variable `foo` is a pointer to a slice of pointers to `FooType` type stuff.
var foo *[]*FooType

// Both are valid.
var greeting string = "hi there"
greetingFollowUp := "how are you"

Go has exceptional variety of basic variable types.

bool        true or false

uint8       the set of all unsigned  8-bit integers (0 to 255)
uint16      the set of all unsigned 16-bit integers (0 to 65535)
uint32      the set of all unsigned 32-bit integers (0 to 4294967295)
uint64      the set of all unsigned 64-bit integers (0 to 18446744073709551615)

int8        the set of all signed  8-bit integers (-128 to 127)
int16       the set of all signed 16-bit integers (-32768 to 32767)
int32       the set of all signed 32-bit integers (-2147483648 to 2147483647)
int64       the set of all signed 64-bit integers
            (-9223372036854775808 to 9223372036854775807)

uint        either 32 or 64 bits, depends on the system
int         same number of bits as `uint` but signed

float32     the set of all IEEE-754 32-bit floating-point numbers
float64     the set of all IEEE-754 64-bit floating-point numbers

complex64   the set of all complex numbers with float32 real and imaginary parts
complex128  the set of all complex numbers with float64 real and imaginary parts

uintptr     an unsigned integer large enough to store the uninterpreted
            bits of a pointer value

string      alias for []byte, a slice of bytes
byte        alias for uint8
rune        alias for int32 (represents a Unicode code point)

new expression to allocate a zeroed value of the requested type. Returns a pointer to the created variable.

package main

import "fmt"

func main() {
    x := new(int)
    // note that it's a pointer, not the value
    // but you can use * to dereference it
    fmt.Printf("%d", *x)  // => 0
}

Variables have a static and dynamic type. Static type is the type specified at the compile time while dynamic type is the runtime current type. interface{} type matches with any other type.

// Non-interface type
var myNumber int = 2 // static: int, dynamic: int

// Interface type
var myValue interface{} = 2 // static: interface{}, dynamic: int
myValue = "foo" // static: interface{}, dynamic: string

Type conversion is done with x.(T). Use variables as a different type.

package main

import "fmt"

type myInteface interface {
    myMethod()
}

func main() {
    // `x` has dynamic type int and value of 7
    // `i` has type int and value 7
    var x interface{} = 123
    var i int = x.(int)
    fmt.Println(i)

    var convertAndPrint = func(toConvert interface{}) {
        // Second return value is a boolean that tells whether the conversion
        // failed or not. On failure, converted value is zero value of
        // the requested type e.g. 0 if int.
        var converted, ok = toConvert.(string)
        if ok {
            fmt.Println("SUCCESS:", converted)
        } else {
            fmt.Println("FAILURE!")
        }

    }

    // Conversion will be ok.
    var yyy interface{} = "stuff!"
    convertAndPrint(yyy)

    // Conversion will fail.
    var myValue myInteface
    convertAndPrint(myValue)

    // THIS WILL PANIC:
    // panic: interface conversion: int is not main.myInteface: missing method myMethod
    var numbers interface{} = 123
    willBreak := numbers.(myInteface)
    fmt.Println(willBreak)
}

Pointers

Arguments passed by value won't mutate the original value. Arguments passed by reference (pointer) will mutate the original value. You can use & to turn a value into a pointer and * to turn a pointer into a value.

package main

import "fmt"

// pass by value, won't mutate the original
func zeroByValue(integerValue int) {
    integerValue = 0
}

// pass by reference, will mutate the original
func zeroByPointer(integerPointer *int) {
    *integerPointer = 0
}

func main() {
    i := 1
    fmt.Println("initial:", i) // => 1
    zeroByValue(i)
    fmt.Println("after zeroByValue:", i) // => 1
    zeroByPointer(&i)
    fmt.Println("after zeroByPointer:", i) // => 0
}

Should I pass by reference or by value? Decision to pass a pointer vs. a value is not about performance, it's about sharing access to the variable. If you want to share the value with a function, then use a pointer. If you want to copy the value, not shared it, use value receivers.

Concurrently called functions requires value receivers. Pointer receivers are not safe for concurrent access. Value receivers are safe for concurrent access. Use channels to provide concurrent access.

Data Structures

Snippets related to data structures:

Arrays are fixed length. Array definition specifies a length and a data type for the elements.

// Note that following two definitions are separate, incompatible types.
[4]int // Array of four integers. Initialized as [0, 0, 0, 0].
[5]int // Array of five integers. Initialized as [0, 0, 0, 0, 0].

var a [4]int
a[0] = 1
i := a[0]
// i == 1
// a[2] == 0, because 0 is the zero value of integer data type
// len(a) == 4

b1 := [2]string{"Penn", "Teller"} // Initializing with values.
b2 := [...]string{"Penn", "Teller"} // Compiler can do the counting for you.
// Type of b2 is [2]string.

Arrays are always one dimensional. You may have arrays inside an array though.

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

Slices are array segments. They behave much like arrays. Slices have data type, length and capacity, but note that slice data types do not specify length or capacity.

// You can use `make` function to create slices.
bytes := make([]byte, 5, 5) // make(Type, Length, Capacity)
// bytes == []byte{0, 0, 0, 0, 0}

// But {} initialization shorthand is more common.
emptySlice := []string{}
letters := []string{"a", "b", "c", "d"}

// You can use slicing so a slice shares the same storage as the original.
moreBytes := []byte{'g', 'o', 'l', 'a', 'n', 'g'}
// moreBytes[1:4] == []byte{'o', 'l', 'a'}
// moreBytes[:2]  == []byte{'g', 'o'}
// moreBytes[2:]  == []byte{'l', 'a', 'n', 'g'}
// moreBytes[:]   == moreBytes

// Arrays can be converted to slices.
// They share the same memory.
myArray := [3]string{"Лайка", "Белка", "Стрелка"}
mySlice := myArray[:]

Slices share memory with the origin array. Modifying elements of a slice modifies the elements of the source. Also, if even one slice exists made from an array, the whole array is kept in the memory.

original := []byte{'r', 'o', 'a', 'd'}

partial := original[2:]
// partial == []byte{'a', 'd'}

partial[1] = 'm'
// partial == []byte{'a', 'm'}
// original == []byte{'r', 'o', 'a', 'm'}

// If you want to have separate memory, use copy.
newOrig := make([]byte, len(original))
copy(newOrig, original)
return newOrig

Slices can be used like dynamic arrays. Built-in functions allow slices to be used like dynamic arrays. But you have total control how they behave.

package main

import "fmt"

func main() {
    // Appending elements to a slice.
    a := []int{0}
    a = append(a, 1, 2, 3)
    fmt.Println(a) // => [0 1 2 3]

    // Appending a slice to another slice.
    b := []string{"John"}
    c := []string{"George", "Ringo", "Pete"}
    b = append(b, c...)
    fmt.Println(b) // => [John George Ringo Pete]

    // Double capacity of a
    fmt.Println(cap(a)) // => 4
    tempA := make([]int, len(a), (cap(a)+1)*2)
    copy(tempA, a)
    a = tempA
    fmt.Println(cap(a)) // => 10
}

All multiline literal items require a trailing comma.

package main

func main() {
    x := []int{
        1,
        2,
    }
    x = x
}

range iterates over arrays, slices, maps and channels.

package main

import "fmt"

func main() {
    var pow = []int{1, 2, 4}
    for i, v := range pow {
        fmt.Printf("2**%d = %d\n", i, v)
    }
    // 2**0 = 1
    // 2**1 = 2
    // 2**2 = 4
}

Maps are unordered groups of keys and values. Also called hash tables or dictionaries.

package main

import "fmt"

func main() {
    // You can use `make` to create maps but literal syntax is more common.
    numbers := map[string]int{
        "one": 1,
        "two": 2,
    }
    numbers["three"] = 3
    fmt.Println("numbers :", numbers)

    // Check for existence and accessing values
    if value, ok := numbers["three"]; !ok {
        fmt.Println("Number three not found!")
    } else {
        fmt.Println("Number three found:", value)
    }

    // {} initialization shorthand is more common.
    states := map[string]string{
        "smile": "happy",
        "frown": "sad",
    }
    fmt.Println("states :", states, "with length", len(states))

    // You can also specify an empty map.
    colors := map[string]string{}
    colors["red"] = "#FF0000"
    colors["green"] = "#00FF00"
    colors["blue"] = "#0000FF"
    fmt.Println("colors :", colors, "with length", len(colors))

    // Removing keys.
    delete(colors, "green")

    // Iterating values.
    // Note that values are returned in a semi-random order
    // because of optimization Go does in the background.
    for key, value := range colors {
        fmt.Println("color loop:", key, value)
    }
}

Getting a single value from a map is faster than from an array. Array must be iterated through, you can just take a value in the middle of the map.

Limiting map capacity makes them a bit more efficient. Otherwise they take a bit more processing power when they are growing. You cannot change the capacity after it has been created.

package main

func main() {
    _ = make(map[string]int, 99)
}

You cannot add items to a nil map. Works with slices, not with maps.

package main

func main() {
    var s []int
    s = append(s, 1)

    // crashes
    // var m map[string]int
    // m["one"] = 1

    var m map[string]int = map[string]int{}
    m["one"] = 1
}

structName{}, struct literals, is used to create structs.

package main

import "fmt"

type Point struct {
    X, Y int
}

var (
    p = Point{1, 2}  // has type Point
    q = &Point{1, 2} // has type *Point
    r = Point{X: 1}  // Y:0 is implicit
    s = Point{}      // X:0 and Y:0
)

func main() {
    fmt.Println(p, q, r, s) // {1 2} &{1 2} {1 0} {0 0}
}

You cannot modify struct elements directly. This is because map elements are not addressable. Use temporary variable or pointer map.

package main

import "fmt"

type data struct {
    name string
}

func main() {
    m1 := map[string]data{"x": {"one"}}
    // m1["x"].name = "two" // would crash
    x := m1["x"]
    x.name = "two"
    m1["x"] = x
    fmt.Println(m1) // => map[x:{two}]

    m2 := map[string]*data{"x": {"one"}}
    m2["x"].name = "two"
    fmt.Println(m2["x"]) // => &{two}
}

interfaces and structs can be used as map keys. They must be a comparable though.

package main

import (
    "fmt"
    "io"
    "os"
)

type BuilderCommit struct {
    builder string
    commit  string
}

type BroadcastWriter struct {
    writers map[io.WriteCloser]struct{}
}

func (bw *BroadcastWriter) AddWriter(writer io.WriteCloser) {
    if bw.writers == nil {
        bw.writers = map[io.WriteCloser]struct{}{}
    }
    bw.writers[writer] = struct{}{}
}

func (bw *BroadcastWriter) Write(p []byte) {
    for w := range bw.writers {
        w.Write(p)
    }
}

func main() {
    bc := BuilderCommit{"linux-amd64", "0cd299"}
    fmt.Println(bc)
    builds := map[BuilderCommit]bool{}
    builds[bc] = true
    fmt.Println(builds)

    bw := BroadcastWriter{}
    bw.AddWriter(os.Stdout)
    bw.Write([]byte("Hello World!"))
}

Locking allows using data structures concurrently. Maps and other more complex structures can be used concurrently by using m.Lock() and m.Unlock(). You should use channels whenever possible though.

package main

import (
    "fmt"
    "sync"
)

var l sync.Mutex
var a string

func f() {
    a = "hello, world"
    l.Unlock()
}

func main() {
    l.Lock()
    go f()
    l.Lock()       // will wait for unlock before continuing
    fmt.Println(a) // hello, world
}

Control Structures

if-statement. Note that else must in the middle of braces or it will not compile.

package main

import "fmt"

func main() {
    myName := "Ruksi"
    if myName == "Tommi" {
        fmt.Println("Hello Tommi!")
    } else if myName == "Ruksi" {
        fmt.Println("Hello Ruksi!")
    } else {
        fmt.Println("Hello!")
    }
}

if-statement may also contain other statements.

package main

import "fmt"

func main() {
    // assignment is made before the conditional
    if myNumber := 9; myNumber < 0 {
        fmt.Println(myNumber , "is negative")
    } else if myNumber < 10 {
        fmt.Println(myNumber , "has 1 digit")
    } else {
        fmt.Println(myNumber , "has multiple digits")
    }
}

switch-statement. Do not indent switch and you do not have not break out of them.

package main

import "fmt"

func main() {
    myNumber := 1
    switch myNumber {
    case 0:
        fmt.Println("ZERO")
    case 1:
        fmt.Println("ONE")
    case 2:
        fmt.Println("TWO")
    default:
        fmt.Println("DUNNO")
    }
}

switch-statement case may have multiple conditions.

package main

import "fmt"

func main() {
    myNumber := 1
    switch myNumber {
    case 0, 1, 2:
        fmt.Println("A FEW")
    default:
        fmt.Println("DUNNO")
    }
}

switch-statements maybe used without an expression. This syntax is frequently used with channel receivers.

package main

import "fmt"

func main() {
    myNumber := 1
    switch {
    case myNumber < 0:
        fmt.Println("NEGATIVE")
    case myNumber == 0, myNumber > 0:
        fmt.Println("NON-NEGATIVE")
    }
}

for-loop.

package main

import "fmt"

func main() {
    for i := 0; i < 3; i++ {
        fmt.Println("i:", i)
    }
}

for-loops work as while-loops.

package main

import "fmt"

func main() {
    j := 2
    for j >= 0 {
        fmt.Println("j:", j)
        j--
    }
}

for-loops work as infinite loops.

package main

import "fmt"

func main() {
    for {
        fmt.Println("This could be infinite!")
        break;
    }
}

for-loops work as for-each-loops.

package main

import "fmt"

func main() {
    numbers := []int{1, 20, 3, 4, 666}
    for _, number := range numbers {
        fmt.Println(number)
    }
}

You can use labeled for-, switch- and select-statements. Labeled breaks and continues are much like goto and can be used sparingly. Labels are not indented.

package main

import "fmt"

func main() {
BrLoops: // => Foo!Bar!
    for x := 0; x < 10; x++ {
        fmt.Print("Foo!")
        for y := 0; y < 10; y++ {
            fmt.Print("Bar!")
            break BrLoops
        }
    }
    fmt.Println()
CntLoops: // => Foo!Bar!Foo!Bar!
    for x := 0; x < 2; x++ {
        fmt.Print("Foo!")
        for y := 0; y < 2; y++ {
            fmt.Print("Bar!")
            continue CntLoops
        }
    }
}

Avoid goto-statements.

package main

import "fmt"

func main() {
    fmt.Print("Hello ")
    goto labelOne
    fmt.Print("I will not print!")
labelOne:
    fmt.Print("World!")
}

Custom Types

Types behave much like classes. All non-interface types may have methods associated with them. You can only define methods on types within the same package.

// Office type string.
type Office string

// Method of Office type string.
func (o Office) GetName() string {
    return o;
}

Type declaration consist of the type name and a type literal.

// type declarations
type Foo struct {
    i int
    s string
}
type Qux []string

// type literals
struct {
    i int
    s string
}
[]string

// anonymous struct
var t struct {
    i int
    s string
}

Create instances with {}, the literal notation. Lower case types are private, upper case types are public.

type Person struct {
    name string
    age  int
}
bob := Person{"Bob", 20}
annPtr := &Person{name: "Ann", age: 40}

struct types have fields and methods.

// House type structure.
type House struct {
    name string
}

// Method of House type structure.
func (h House) GetName() string {
    return h.name;
}

structs are comparable if all its fields are comparable. And the fields exist in both structs.

package main

import "fmt"

func main() {
    var q struct{ s string }
    var r struct{ s string }
    var c struct {
        s string
        t int
    }
    fmt.Println(q == r) // true
    q.s = "hello"
    fmt.Println(q == r) // false
    r.s = "hello"
    fmt.Println(q == r) // true
    fmt.Println(c)
    // fmt.Println(q == c) won't work, non-matching fields
}

Interfaces only specify required methods. Types implement an interface by having the methods described by the interface definitions. You do not need to explicitly state that it implements the interface.

package main

import "fmt"

// Something can be considered a `FooBaz` if it can `Foo` and `Baz`.
type FooBaz interface {
    Foo()
    Baz() int
}

// Example structure is considered `FooBaz` as it implements `Foo` and `Baz`.
type Example struct {
    importantNumber int
}

func (e Example) Foo() {
    fmt.Println("I'm an example! Foo.")
}
func (e Example) Baz() int {
    fmt.Println("I'm an example! Baz.")
    return e.importantNumber
}

func MakeItYell(fooBaz FooBaz) {
    fooBaz.Foo()
    fmt.Println(fooBaz.Baz())
}

func main() {
    var fooBaz = Example{10}
    MakeItYell(fooBaz)
}

Don't require broader interface than you need. Function should only accept the minimal interface they need.

Everything implements an empty interface.

// This accepts any parameter.
func DoSomething(v interface{}) {
   // ...
}

Function types. Usually used to specify callback function format.

package main

import "fmt"

type operation struct {
    name string
    fn func(int, int) int // Function that takes 2 integers and gives one.
}

func main() {
    // Creating functions that fit the function type.
    operations := []operation{
        {"add", func(x, y int) int { return x + y }},
        {"sub", func(x, y int) int { return x - y }},
        {"mul", func(x, y int) int { return x * y }},
        {"div", func(x, y int) int { return x / y }},
        {"mod", func(x, y int) int { return x % y }},
    }

    // Pick one of those operations.
    selectedOperation := operations[2]

    x, y := 5, 12
    fmt.Println( selectedOperation.name, x, y )
    fmt.Println( selectedOperation.fn(x, y) )
}

Prefer functions over methods. People from OO background overuse methods. Functions should never depend on a state of a structure. Same input will always produce same output. Methods should depend on the state of a structure.

Methods are bound to a specific type.
Functions accept interfaces as input.

Composition works much like inheritance. You can implement inheritance-like behavior with embedding.

package main

import "fmt"

type Person struct {
    Name string
}

func (p *Person) GetName() string {
    return p.Name
}

// By not giving a name to Person property, we are using embedding.
// You can then call Employee.GetName()
type Employee struct {
    Title string
    Person
}

func (e *Employee) GetTitle() string {
    return e.Title
}

func main() {
    joe := Person{"Joe"}
    fmt.Println(joe.GetName())
    alice := Employee{"Manager", Person{"Alice"}}
    fmt.Println(alice.GetName(), "(", alice.GetTitle(), ")")
}

Functions

Functions that are not associated with any type are called top-level functions.

package main

import "fmt"

// accessible outside the package
func PublicTopLevelFunction(text string) string {
    fmt.Println(text)
    return text
}

// not accessible outside the package
func privateTopLevelFunction(text string) string {
    fmt.Println(text)
    return text
}

func main() {
    PublicTopLevelFunction("hello!")
    privateTopLevelFunction("hello!")
    indirectTopLevel := privateTopLevelFunction
    indirectTopLevel("indirect hi!")
}

Functions can take any number of trailing arguments with ... Also called variadic functions.

package main

import "fmt"

func sumPrint(nums ...int) {
    fmt.Print(nums, " ")
    total := 0
    for _, num := range nums {
        total += num
    }
    fmt.Println(total)
}

func main() {
    sumPrint(1, 2)              // => [1 2] 3
    sumPrint(1, 2, 3)           // => [1 2 3] 6
    nums := []int{1, 2, 3, 4}
    sumPrint(nums...)           // => [1 2 3 4] 10
}

Functions can return multiple values.

package main

import (
    "fmt"
    "math/rand"
    "errors"
    "time"
)

func badWords(text string) (string, error) {
    var err error
    if (rand.Intn(2) == 1) {
        err = errors.New("Bad word found!")
    }
    return text, err
}

func main() {
    rand.Seed( time.Now().UTC().UnixNano() )
    filtered, err := badWords("Kinky!")
    if err != nil {
        fmt.Println("Error: " + err.Error())
        return
    }
    fmt.Println(filtered)
}

Function literals are defined using func without a name. They can be saved to variables, passed as argument and returned from a function.

package main

import "fmt"

func main() {
    var functionLiteral = func(text string) {
        fmt.Println(text)
    }
    functionLiteral("halo!")

    indirectLiteral := functionLiteral
    indirectLiteral("indirect halo!")

    func() {
        fmt.Println("wow! immediate function! so javascript!")
    }()
}

Function closures. Functions may close over variables, forming a closure.

package main

import "fmt"

func intSequence() func() int {
    i := 0
    return func() int {
        i += 1
        return i
    }
}

func main() {
    nextId := intSequence()
    fmt.Println(nextId())
    fmt.Println(nextId())
    fmt.Println(nextId())

    nextType := intSequence()
    fmt.Println(nextType())
}

Methods are created by associating the function with a type. Methods can be defined with value or pointer receiver.

package main

import "fmt"

type MyType struct {}

func (mt MyType) MethodWithValueReceiver(text string) {
    fmt.Println(text + ", value receiver!")
}

func (mt *MyType) MethodWithPointerReceiver(text string) {
    fmt.Println(text + ", pointer receiver!")
}

func main() {
    // Usage:
    var t1 MyType
    t1.MethodWithValueReceiver("normal call")
    t1.MethodWithPointerReceiver("normal call")

    // Or, staticly:
    MyType.MethodWithValueReceiver(t1, "static call")
    (*MyType).MethodWithPointerReceiver(&t1, "static call")

    // Or, indirectly:
    indirectValueReceiver := MyType.MethodWithValueReceiver
    indirectValueReceiver(t1, "indirect call")
    indirectPointerReceiver := (*MyType).MethodWithPointerReceiver
    indirectPointerReceiver(&t1, "indirect call")
}

Prefer pointer receivers to value receivers. Otherwise the structure is passed by value and the original cannot be edited. Passing by value is also more expensive operation because the value must be copied before passing.

package main

import "fmt"

type Person struct {
    name string
    age int
}

// Passing by value, suboptimal.
func (p Person) ChangeNameByValue(str string) {
    p.name = str
}

// Passing by reference, nice work!
func (p* Person) ChangeNameByPointer(str string) {
    p.name = str
}

func (p* Person) GetName() string {
    return p.name
}

func main() {
    var bob = Person{"Bob", 20}
    bob.ChangeNameByValue("Jake")
    fmt.Println(bob.GetName()) // => Bob
    bob.ChangeNameByPointer("Jake")
    fmt.Println(bob.GetName()) // => Jake
}

Interfaces are collections of function signatures. Type methods can also be called through interfaces.

package main

import "fmt"

type MyInterface interface {
    MethodName(string)
}

type MyType struct {}
func (MyType) MethodName(x string) {
    fmt.Println("1: " + x)
}

type MyTypeToo struct {}
func (*MyTypeToo) MethodName(x string) {
    fmt.Println("2: " + x)
}

func main() {
    var t MyType
    t.MethodName("method call")

    var tt MyTypeToo
    tt.MethodName("method call")

    var i MyInterface = t
    i.MethodName("call to interface.")
    MyInterface.MethodName(t, "static call to interface.")

    i = &tt
    i.MethodName("call to the same interface.")
    MyInterface.MethodName(&tt, "static call to the same interface.")
}

All custom types can have methods. Even function types can have methods.

package main

import "fmt"

type number int

func (n number) IntegerMethodWhat() {
    fmt.Println(n)
}

type FunctionType func(text string) (string)

func (FunctionType) MethodName(x string) {
    fmt.Println(x)
}

func main() {
    var x number = 1337
    x.IntegerMethodWhat() // => 1337

    var t1 FunctionType = func(text string) (string) {
        return text
    }
    t1.MethodName( t1("INCEPTION!") ) // => INCEPTION!
}

Defer

defer-statement is commonly used for clean-up. defer-statement pushes a function call to be executed after the surrounding function returns.

func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }
    defer src.Close() // Always closed on return.

    dst, err := os.Create(dstName)
    if err != nil {
        return
    }
    defer dst.Close() // Always closed on return.

    return io.Copy(dst, src)
}

Deferred function arguments are evaluated at the point of defer-statement.

package main

import "fmt"

func deferEval() {
    i := 0
    defer fmt.Println(i)
    i = 100
    fmt.Print("i prints to ")
}

func main() {
    deferEval() // => i prints to 0 (not 100)
}

Deferred functions are executed in Last In First Out order.

package main

import "fmt"

func deferOrder() {
    for i := 0; i < 4; i++ {
        defer fmt.Print(i, " ")
    }
}

func main() {
    deferOrder() // => 3 2 1 0
}

Deferred functions may modify the surrounding function's named return values.

package main

import "fmt"

func deferChangeReturn() (i int) {
    defer func() {
        i++
    }()
    return 1
}

func main() {
    fmt.Println( deferChangeReturn() ) // => 2
}

Deferred functions may recover from panics. Panic will fail the surrounding function, but normal execution will continue outside the function.

package main

import "fmt"

func recoverThis() (response string) {
    defer func() {
        if msg := recover(); msg != nil {
            fmt.Println("Recovered from", msg)
        }
        response = "recovered!"
    }()
    panic("A MAJOR PANIC!")
    fmt.Println("This will not be printed.")
    return "wat"
}

func main() {
    // You could recover here also!
    value := recoverThis() // => Recovered from A MAJOR PANIC!
    fmt.Println(value)     // => recovered!
}

Goroutines

Snippets related to goroutines:

Goroutine is like a lighweight thread. Concurrency in Go is done by running normal functions as goroutines. The go keyword runs given functions as usual, but the caller does not wait. But note that if main() routine returns, all execution stops.

package main

import (
    "fmt"
    "time"
)

func Log(s string) {
    fmt.Println(s)
}

func main() {
    go Log("This comes between sleep and awake.")
    Log("Sleep!")
    time.Sleep(2 * time.Second)
    Log("Awake!")
}

Goroutines' main way of communication is channels. You can also use locked data structures but that is more troublesome to maintain deadlock-free.

package main

import "fmt"

func main() {
    messages := make(chan string) // Create a channel.
    go func() {
        messages <- "ping" // Sends to channel.
    }()
    msg := <-messages // Waits to receive form channel.
    fmt.Println(msg)
}

Goroutines are really cheap. It is common to have hundreds of thousands goroutines running.

Channels

Snippets related to channels:

Channels are used to communicate between two or more goroutines. They are typed and usually passed as the function parameter.

// Create an unbuffered channel.
numberChannel := make(chan int)

// Send a value to a channel, and because unbuffered, waits receiver to appear.
numberChannel <- 1

// Waits to receive a value from a channel.
<-numberChannel

// Waits to receive a value from a channel and assigns it.
value := <-numberChannel

// Waits to receive a value from a channel.
// `ok` will be false if channel is closed
value, ok := <-numberChannel

// Variable n is going to be 3 after this.
myChannel := make(chan int)
go func() {
    myChannel <- 3
}()
n := <-myChannel

// Channels can contain channels.
inceptionChannel := make(chan chan string)

Channel buffered size defines the number of elements that can be sent on the channel at the same time. They will keep their order in the channel. Sending to a buffered channel block only when the buffer is full. By default buffer size is 0 so every send will block.

// Channel with buffer size of 0.
a := make(chan int)
a <- 1 // Blocks execution until other process receives from the channel.

// Channel with buffer size of 2.
b := make(chan int, 2)
b <- 1 // Doesn't block.
b <- 2 // Doesn't block.
b <- 3 // Blocks execution until other process receives from the channel.

You can limit the communication direction in type definition.

func iCanOnlyReadFromThisChannel(input <-chan int) {}
func iCanOnlySendToThisChannel(output chan<- int) {}

range consumes all data from a channel. Listens until the channel is closed with close function.

package main

import (
    "fmt"
    "time"
)

func printAsLongAsOpen(numbers chan int, done chan bool) {
    for i := range numbers {
        time.Sleep(time.Second) // Emulate that each process takes 1 second.
        fmt.Println(i)
    }
    done <- true
}

func main() {
    numbers := make(chan int)
    done := make(chan bool)
    go printAsLongAsOpen(numbers, done)
    go printAsLongAsOpen(numbers, done)
    numbers <- 1
    numbers <- 2
    time.Sleep(5 * time.Second) // Gives the number 3 after 5 seconds.
    numbers <- 3
    close(numbers) // Closes the channel so goroutine gets out of the for-loop.
    <-done // Waits for the goroutine to finish.
}

Only the sender should close a channel, never the receiver.

close(ch)

Keep in mind that main process can still quit range prematurely.

// bad
package main

import "fmt"

func main() {
    ch := make(chan string)
    go func() {
        for m := range ch {
            fmt.Println("processed:", m)
        }
    }()
    ch <- "cmd.1"
    ch <- "cmd.2" // won't be processed as main exits right after
}
// good
package main

import (
    "fmt"
    "sync"
)

func main() {
    ch := make(chan string)
    wg := sync.WaitGroup{}
    wg.Add(1)
    go func(wg *sync.WaitGroup) {
        defer wg.Done()
        for m := range ch {
            fmt.Println("processed:", m)
        }
    }(&wg)
    ch <- "cmd.1"
    ch <- "cmd.2"
    close(ch) // stops the `range` from looping
    wg.Wait() // waits for the goroutine to finish
}

for- and select-statements are used to listen multiple channels. Go has a control structure called select which works as a channel multiplexer, allowing interaction with multiple channel operations or channels. Using just for keeps the code running in infinite loop until broken out of.

package main

import (
    "fmt"
    "time"
)

func main() {
    c1 := make(chan string)
    c2 := make(chan string)

    go func() {
        time.Sleep(time.Second * 1)
        c1 <- "one"
    }()
    go func() {
        time.Sleep(time.Second * 2)
        c2 <- "two"
    }()
    go func() {
        for {
            msg := <-c2
            fmt.Println(msg)
            time.Sleep(time.Second * 4)
        }
    }()

    for i := 0; i < 4; i++ {
        select {
        case c2 <- "HELLO": // Sends HELLO if someone is receiving from channel.
        case msg1 := <-c1:  // Receives from c1 if someone is sending.
            fmt.Println("received", msg1)
        case msg2 := <-c2:  // Receives from c2 if someone is sending.
            fmt.Println("received", msg2)
        default:
            fmt.Println("no activity")
        }
    }
}

The default case in select is run if no other case is ready.

package main

import (
    "fmt"
    "time"
)

func main() {
    tick := time.Tick(100 * time.Millisecond)
    boom := time.After(500 * time.Millisecond)
    for {
        select {
        case <-tick:
            fmt.Println("tick.")
        case <-boom:
            fmt.Println("BOOM!")
            return
        case <-time.After(100 * time.Millisecond):
            fmt.Printf("Timed out!")
        default:
            fmt.Println(" .")
            time.Sleep(50 * time.Millisecond)
        }
    }
}

Documentation

Use godoc documntation tool and syntax. godoc parses source code and produces documentation as plain text or HTML.

Documentation should be full sentences and start with the entity name. You document types, variables, constants, functions and packages with a regular comment directly preceding its declaration.

// Fprint formats using the default formats for its operands and writes to w.
// Spaces are added between operands when neither is a string.
// It returns the number of bytes written and any write error encountered.
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
    // ...
}

First line of package comment is shown in package list.

// Package sort provides primitives for sorting slices and user-defined
// collections.
package sort

Comments that are not adjacent to a top-level declaration are omitted. Except those that start with BUG(who).

// BUG(r): Title uses for word boundaries does not handle Unicode punctuation.

Blank lines can be used to separate paragraphs.

/*
Package gob manages streams of gobs - binary values exchanged between an
Encoder (transmitter) and a Decoder (receiver).  A typical use is transporting
arguments and results of remote procedure calls (RPCs) such as those provided by
package "net/rpc".

The implementation compiles a custom codec for each data type in the stream and
is most efficient when a single Encoder is used to transmit a stream of values,
amortizing the cost of compilation.
*/

Pre-formatted text is indented relative to surrounding comment.

/*
can be sent from or received into any of these Go types:

    struct { A, B int }     // the same
    *struct { A, B int }    // extra indirection of the struct
*/

URLs are automatically converted to links in HTML output.

Performance

Go uses only one OS thread by default. You can change this with runtime.GOMAXPROCS(), even though they don't straight up convert of CPUs used.

package main

import (
    "fmt"
    "runtime"
)

var _ = runtime.GOMAXPROCS(1)

func main() {
    fmt.Println(runtime.GOMAXPROCS(-1)) // => 1
    fmt.Println(runtime.NumCPU())       // => 1 (on play.golang.org)
    runtime.GOMAXPROCS(20)
    fmt.Println(runtime.GOMAXPROCS(-1)) // => 20
    runtime.GOMAXPROCS(300)
    fmt.Println(runtime.GOMAXPROCS(-1)) // => 256
}

Packages

Sources