Cheatsheets
 / Go
↑ ↓ to navigate     ↵ to select     Esc to close
suggest edit
Basics
Intro
Variables
Constants
Basic types
Operators
Strings
Numbers
Arrays
Slices
Maps
Pointers
Type conversions
Structs
Functions
Methods
Flow control
if
switch
for / range / while loop
defer
panic and recover
Packages
Importing
Aliases
Exporting names
Packages
Concurrency
Goroutines
Buffered channels
Closing channels
WaitGroup
Race detector
Advanced
Interfaces
Testing
Go CLI
Standard libs
fmt
os

Basics

IntroVariablesConstantsBasic typesOperatorsStringsNumbersArraysSlicesMapsPointersType conversionsStructsFunctionsMethods

Intro

hello_world.go:

package main

import "fmt"

func main() {
  message := greetMe("world")
  fmt.Println(message)
}

func greetMe(name string) string {
  return "Hello, " + name + "!"
}

Run it with: go run hello_world.go To build as an executable:

Resources

Or try it out in the Go repl, or A Tour of Go.

Variables

** Variable declaration**

var msg string
msg = "Hello"

** Shortcut declaration (Infers string type from value)**

msg := "Hello"

Constants

const Phi = 1.618

Constants can be character, string, boolean, or numeric values.

See: Constants

Basic types

Type Set of Values Values
bool boolean true/false
string array of characters needs to be inside “”
int integers 32 or 64 bit integer
int8 8-bit integers [ -128, 128 ]
int16 16-bit integers [ -32768, 32767]
int32 32-bit integers [ -2147483648, 2147483647]
int64 64-bit integers [ -9223372036854775808, 9223372036854775807 ]
uint8 8-bit unsigned integers [ 0, 255 ]
uint16 16-bit unsigned integers [ 0, 65535 ]
uint32 32-bit unsigned integers [ 0, 4294967295 ]
uint64 64-bit unsigned integers [ 0, 18446744073709551615 ]
float32 32-bit float
float64 64-bit float
complex64 32-bit float with real and imaginary parts
complex128 64-bit float with real and imaginary parts
byte sets of bits alias for uint8
rune Unicode characters alias for int32

Operators

Arithmetic Operators:

Symbol Operation Valid Types
+ Sum integers, floats, complex values, strings
- Difference integers, floats, complex values
* Product integers, floats, complex values
/ Quotient integers, floats, complex values
% Remainder integers
& Bitwise AND integers
` ` Bitwise OR
^ Bitwise XOR integers
&^ Bit clear (AND NOT) integers
<< Left shift integer << unsigned integer
>> Right shift integer >> unsigned integer

Comparison Operators:

Symbol Operation
== Equal
!= Not equal
< Less
<= Less or equal
> Greater
>= Greater or equal

Logical Operators:

Symbol Operation
&& Conditional AND
` `
! NOT

Strings

str := "Hello"
str := `Multiline
string`

Strings are of type string.

Numbers

Typical types:

num := 3          // int
num := 3.         // float64
num := 3 + 4i     // complex128
num := byte('a')  // byte (alias for uint8)

Other types:

var u uint = 7        // uint (unsigned)
var p float32 = 22.7  // 32-bit float

Arrays

// var numbers [5]int
numbers := [...]int{0, 0, 0, 0, 0}

Arrays have a fixed size. They are rarely used explicitly. Instead you use slices which are a view onto underlying array.

Slices

slice := []int{2, 3, 4}
slice := []byte("Hello")

Slices have a dynamic size, unlike arrays.

Maps

Maps are key / value dictionsries

// declare a map, has value of nil
var cities map[string]string

// must create a map before using
cities = map[string]string{}

// insert value
cities["NY"] = "EUA"

// retrieve
newYork = cities["NY"] // returns "EUA"

// delete
delete(cities, "NY")

// check if a key is setted
if value, ok := cities["NY"]; ok {
    println("found key 'NY' in map")
}

// iterate over keys and values:
for key, value := range cities {
    println("key:", key, "value:", value)
}

Pointers

Pointers point to a memory location of a variable.

func main () {
  b := *getPointer()
  fmt.Println("Value is", b)
}
func getPointer () (myPointer *int) {
  a := 234
  return &a
}
a := new(int)
*a = 234

Unlike C++, Go doesn’t have pointer arithmetic (you can’t add a number to a pointer to change the pointer).

See: Pointers

Type conversions

i := 2
f := float64(i)
u := uint(i)

See: Type conversions

Structs

type Vertex struct {
  X int
  Y int
}
func main() {
  v := Vertex{1, 2}
  v.X = 4
  fmt.Println(v.X, v.Y)
}

See: Structs

Literals

v := Vertex{X: 1, Y: 2}
// Field names can be omitted
v := Vertex{1, 2}
// Y is implicit
v := Vertex{X: 1}

You can also put field names.

Pointers to structs

v := &Vertex{1, 2}
v.X = 2

Doing v.X is the same as doing (*v).X, when v is a pointer.

Functions

Lambdas

myfunc := func() bool {
  return x > 10000
}

Functions are first class objects.

Multiple return types

a, b := getMessage()
func getMessage() (a string, b string) {
  return "Hello", "World"
}

Named return values

func split(sum int) (x, y int) {
  x = sum * 4 / 9
  y = sum - x
  return
}

By defining the return value names in the signature, a return (no args) will return variables with those names.

See: Named return values

Methods

Receivers

type Vertex struct {
  X, Y float64
}
func (v Vertex) Abs() float64 {
  return math.Sqrt(v.X * v.X + v.Y * v.Y)
}
v := Vertex{1, 2}
v.Abs()

There are no classes, but you can define functions with receivers.

See: Methods

Mutation

func (v *Vertex) Scale(f float64) {
  v.X = v.X * f
  v.Y = v.Y * f
}
v := Vertex{6, 12}
v.Scale(0.5)
// `v` is updated

By defining your receiver as a pointer (*Vertex), you can do mutations.

See: Pointer receivers

Flow control

ifswitchfor / range / while loopdeferpanic and recover

if

if day == "sunday" || day == "saturday" {
  rest()
} else if day == "monday" && isTired() {
  groan()
} else {
  work()
}

See: If

Statements in if:

if _, err := doThing(); err != nil {
  fmt.Println("Uh oh")
}

A condition in an if statement can be preceded with a statement before a ;. Variables declared by the statement are only in scope until the end of the if.

See: If with a short statement

switch

switch n {
    case 2, 3, 5:
        println("n is prime number")
    case 1:
        // cases don't "fall through" by default but you
        // can do it explicitly:
        fallthrough
    case 3:
        println("n is either 1 or 3")
    default:
        println("all other values of n")
}

See: Switch

Type switch:

func printV(v interface{}) {
    switch rv := v.(type) {
        case int:
            println("v is of type int and has value of", rv)
        case *Foo:
            println("v is a pointer to struct Foo")
    }
}

for / range / while loop

for count := 0; count <= 10; count++ {
  fmt.Println("My counter is at", count)
}

See: For loops

entry := []string{"Jack","John","Jones"}
for i, val := range entry {
  fmt.Printf("At position %d, the character %s is present\n", i, val)
}

See: For-Range loops

Go doesn’t have while keyword but you use for:

n := 0
x := 42
for n != x {
  n := guess()
}

See: Go’s “while”

Use break to exit loop:

i := 0
for {
    i++
    if (i > 10) {
        break
    }
}

Forever loop:

for {
}

defer

func main() {
  defer fmt.Println("Done")
  fmt.Println("Working...")
}

Defers running a function until the surrounding function returns. The arguments are evaluated immediately, but the function call is not ran until later.

See: Defer, panic and recover

Deferring functions

func main() {
  defer func() {
    fmt.Println("Done")
  }()
  fmt.Println("Working...")
}

Lambdas are better suited for defer blocks.

func main() {
  var d = int64(0)
  defer func(d *int64) {
    fmt.Printf("& %v Unix Sec\n", *d)
  }(&d)
  fmt.Print("Done ")
  d = time.Now().Unix()
}

The defer func uses current value of d, unless we use a pointer to get final value at end of main.

panic and recover

panic() is similar to throwing an exception. It’s used extremely rarely in Go, typically to signal a condition so bad that it should exit the program.

Unhandled, it’ll exit the program with error code and print a callstack for debugging.

You can catch a panic with recover:

package main

import "fmt"

func main() {
    f()
    fmt.Println("Returned normally from f.")
}

func f() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()
    fmt.Println("Calling g.")
    g(0)
    fmt.Println("Returned normally from g.")
}

func g(i int) {
    if i > 3 {
        fmt.Println("Panicking!")
        panic(fmt.Sprintf("%v", i))
    }
    defer fmt.Println("Defer in g", i)
    fmt.Println("Printing in g", i)
    g(i + 1)
}

Packages

ImportingAliasesExporting namesPackages

Importing

import "fmt"
import "math/rand"
import (
  "fmt"        // gives fmt.Println
  "math/rand"  // gives rand.Intn
)

Both are the same.

See: Importing

Aliases

import r "math/rand"
r.Intn()

Exporting names

func Hello () {
  ···
}

Exported names begin with capital letters.

See: Exported names

Packages

package hello

Every package file has to start with package.

Concurrency

GoroutinesBuffered channelsClosing channelsWaitGroupRace detector

Goroutines

func main() {
  // A "channel"
  ch := make(chan string)

  // Start concurrent routines
  go push("Moe", ch)
  go push("Larry", ch)
  go push("Curly", ch)

  // Read 3 results
  // (Since our goroutines are concurrent,
  // the order isn't guaranteed!)
  fmt.Println(<-ch, <-ch, <-ch)
}
func push(name string, ch chan string) {
  msg := "Hey, " + name
  ch <- msg
}

Channels are concurrency-safe communication objects, used in goroutines.

See: Goroutines, Channels

Buffered channels

ch := make(chan int, 2)
ch <- 1
ch <- 2
ch <- 3
// fatal error:
// all goroutines are asleep - deadlock!

Buffered channels limit the amount of messages it can keep.

See: Buffered channels

Closing channels

Closes a channel:

ch <- 1
ch <- 2
ch <- 3
close(ch)

Iterates across a channel until its closed:

for i := range ch {
  ···
}

Closed if ok == false:

v, ok := <- ch

See: Range and close

WaitGroup

import "sync"

func main() {
  var wg sync.WaitGroup
  
  for _, item := range itemList {
    // Increment WaitGroup Counter
    wg.Add(1)
    go doOperation(item)
  }
  // Wait for goroutines to finish
  wg.Wait()
  
}
func doOperation(item string) {
  defer wg.Done()
  // do operation on item
  // ...
}

A WaitGroup waits for a collection of goroutines to finish. The main goroutine calls Add to set the number of goroutines to wait for. The goroutine calls wg.Done() when it finishes. See: WaitGroup

Race detector

Running code concurrently introduces a new class of bugs: modyfing memory from multiple goroutines. This is known as a data race.

To catch data races, build with -race flag (i.e. go build -race or go run -race).

This compiles the code with additional instrumentation that detects data races and aborts the program when that happens (so that you can fix the bug that caused data race).

Advanced

InterfacesTestingGo CLI

Interfaces

An interface defines a set of methods. Any struct implementing those methods can be used as a value of the interface

Interface definition:

type Shape interface {
  Area() float64
  Perimeter() float64
}

Struct Rectangle implicitly implements interface Shape by implementing all of its methods.

type Rectangle struct {
  Length, Width float64
}

func (r Rectangle) Area() float64 {
  return r.Length * r.Width
}

func (r Rectangle) Perimeter() float64 {
  return 2 * (r.Length + r.Width)
}

The methods defined in Shape are implemented in Rectangle.

Using interface:

func printShapeInfo(s Shape) {
  fmt.Printf("Type of r: %T, Area: %v, Perimeter: %v.", s, s.Area(), s.Perimeter())
}

func main() {
  r := Rectangle{Length: 3, Width: 4}
  printArea(r)
}

Testing

Go has a built-in support for testign. Test functions must be named Test*(t *testing.T) and placed in *_test.go files.

Example: file main.go with function Sum to be tested:

func Sum(x, y int) int {
    return x + y
}

File main_test.go with TestSum function testing Sum:

import ( 
    "testing"
    "reflect"
)

// must s
func TestSum(t *testing.T) {
    x, y := 2, 4
    expected := 2 + 4

    if !reflect.DeepEqual(sum(x, y), expected) {
        t.Errorf("Function Sum not working as expected")
    }
}

Running tests:

Go CLI

# Compile & Run code
$ go run [file.go]

# Compile
$ go build [file.go]
# Running compiled file
$ ./hello

# Test packages
$ go test [folder]

# Install packages/modules
$ go install [package]

# List installed packages/modules
$ go list

# Update packages/modules
$ go fix

# Format package sources
$ go fmt

# See package documentation
$ go doc [package]

# Add dependencies and install
$ go get [module]

# See Go environment variables
$ go env

# See version
$ go version

Standard libs

fmtos

fmt

Commonly used: Printf, Errorf, Sprintf, official docs

import "fmt"

fmt.Printf("%s is %d years old\n", "John", 32) // Print with formatting
fmt.Errorf("User %d not found", 123) // Print a formatted error
s := fmt.Sprintf("Boolean: %v\n", true) // format to a string

os

Commonly used: Chdir, Mkdir, MkdirAll, ReadFile, Remove, RemoveAll, Rename, WriteFile, official docs

import "os"

err := os.Mkdir("dir", 0755)
err = os.RemoveAll("dir")

d := []byte("my data")
err = os.WriteFile("file.txt", d, 0644)

d, err = os.ReadFile("file.txt")
fmt.Printf("Content of file.txt:\n%s\n", string(d))