· 8 min read

Coding in Go

Notably, this is not the most comprehensive section of my Coding Commonplace. That’s odd because I’ve used Go more than any other language in my professional programming career. Well over a million lines of code at this point.

What I’ve not done is write much about it. This page should start to change that.


Table of Contents

Init

1. Installation

Edit (2025): DO NOT install Go like this. Instead, download a tar of Go as a binary and add it to your system $PATH.

To install go version 1.13 on linux:

sudo apt-get upgrade
sudo apt-get update
sudo apt-get install golang-go

After resetting the terminal window, run go version to verify that this worked properly.

To uninstall golang with sudo, use

sudo apt-get remove golang-go
sudo apt-get remove --auto-remove golang-go

Q: When might you want to uninstall golang on Ubuntu? A: If you find that go version and sudo go version are returning different results, you most likely have installed using sudo apt-get and it’s causing problems.

Installation

Ref: https://sal.as/post/install-golan-on-wsl/

Go Basics for Anki

Write a hello world golang program:

// hello.go
package main

import "fmt"

func main() {
  fmt.Println("Hello, 世界")
}

There’s a similar function to fmt.Println called println. Why use fmt?

In the “Bootstrapping” section of the spec, it says that “Current implementations provide several built-in functions useful during bootstrapping. These functions are documented for completeness but are not guaranteed to stay in the language. They do not return a result.” “Thus, [functions like println that are built into the language] are useful to developers because they lack dependencies (being built into the compiler) but not in production code. It also important to note that print and println report to stderr, not stdout.” - Alexander on StackOverflow

Q: Run a golang code file named hello.go without building. Use go run hello.go at the command prompt.

Q: Run a golang code file, hello.go, saving the compiled result for later use. Use go build hello.go.

Q: After compiling with go build hello.go, run the file by… executing the binary file with ./hello at the prompt

Q: Import the packages for dealing with the operating system and and printing formatted output.

import (
  "fmt"
  "os"
)

All of your projects live in the same place, your workspace. By default, this is a folder called “go” in your user directory. You can check this location by running go env GOPATH.

Functions start with the func keyword.

Write a function that sums two integers.

func sum(x: int, y: int) int {
  return x + y
}

Initialize a variable powerLevel to 5 with explicit typing.

var powerLevel int = 5

Initialize a variable powerLevel to 5 with implicit typing.

powerLevel := 5

Write conditional logic that prints “oof” if x is greater than 0.

if x > 0 {
  fmt.Println("oof")
}

Write conditional logic that prints “whale” if x is greater than 10, “minnow” if x is less than 1, and “shark” otherwise.

if x > 10 {
  fmt.Println("whale")
} else if x < 1 {
  fmt.Println("minnow")
} else {
  fmt.Println("shark")
}

Create and an array, “point”, that holds 2 ints.

var point [2]int

Create a slice of ints called “arr”.

var arr []int

What will this function return?

func foo() {
  var arr []int
  return arr
}

A: An empty slice, [].

Write the type definition for a dictionary that maps strings to integers. map[string]int

Write the type definition for a dictionary that maps wallet (Wallet) to balance (int). map[Wallet]int

Declare a dictionary variable, strIntMap, that maps strings to integers.

var strIntMap map[string]int

Def: To Declare a variable is to introduce the variable to the program by defining its type and name.

Assigning to a variable means providing the variable with a value.

To initialize a variable is to assign the variable with an intitial value.

Instantiate means to “create an instance of”.

Will the following code run?

func main() {
  var facts map[int]string
  facts[0] = "False"
  facts[1] = "True"
  fmt.Println(facts)
}

A: No, you’ll get panic: assignment to entry in nil map because that’s not how to initialize a map in go. You must use make: make(map[int]string).

Initialize a dictionary, goku, that maps “powerLevel” to 9001.

var goku = make(map[string]int)
goku["powerLevel"] = 9001

Q: What’s another way to write this that will work?

var facts = make(map[int]string)

A:

facts := make(map[int]string)

How do you delete the “age” key-value pair?

var goku = make(map[string]int)
goku["powerLevel"] = 9001
goku["age"] = 40

A: delete(goku, "age")

Cl: The delete method operates on maps.

Q: Print the values 0 through 4 going incrementing by 1.

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

Q: Initalize a slice, letters, containing the letters “a”, “b”, “c”

arr := []string{"a", "b", "c"}
// var arr = []string{"a", "b", "c"}

Q: Given that arr := []string{"a", "b", "c"}, iterate through the slice printing the indices and values.

for index, value := range arr {
  fmt.Println(index, value)
}

Given that

goku := make(map[string]int)
goku["powerLevel"] = 9001
goku["age"] = 40

Iterate through the dictionary printing the keys and values.

for key, value := range goku {
  fmt.Println(key, value)
}

Q: What method allows you to take the square root of a number? A: math.Sqrt

Q: Create a “person” struct with name (string) and age (int) fields.

type person struct {
  name string
  age int
}

Given the “person” struct, initialize Naruto at age 22.

type person struct {
  name string
  age int
}
naruto := person{name: "Naruto", age: 22}
fmt.Println(naruto)

Q: If I give you a variable x := 7, how do you find its memory address? A: &x

Structures

Q: GO is not an object-oriented language like C++ or Java.

Q: Define a Saiyan structure with a name (string) and power (int).

A:

type Saiyan struct {
    name string
    power int
}

Q: Initialize a Saiyan, “Goku”, with a power of 9001.

type Saiyan struct {
    name string
    power int
}

A:

goku := Saiyan{
    name: "Goku",
    power: 9001,
}

Q:

type Saiyan struct {
    name string
    power int
}

Is it valid to write the following?

goku := Saiyan{}
// or
goku := Saiyan{name: "Goku"}

A: Yes, structures can have unassigned fields just like variables can have unassigned values.

Cl: Structures can have unassigned fields just like variables can have unassigned values.

Cl: Go passes arguments to a function as copies.

Q: What does the following print?

package main

import "fmt"

type Saiyan struct {
  name  string
  power int
}

func main() {
  goku := Saiyan{"Goku", 9000}
  Super(goku)
  fmt.Println(goku.power)
}

func Super(s Saiyan) {
  s.power *= 2
}

A: Still 9000 because only the copy of goku became super, not the original variable. Changes made in Super weren’t reflected in the caller.

Q:

type Saiyan struct {
  name  string
  power int
}

func main() {
  goku := Saiyan{"Goku", 9000}
  Super(goku)
  fmt.Println(goku.power)
}

func Super(s Saiyan) {
  s.power *= 2
}

Q: If you have an interface, Spec, and want to verify that the type, MyType, implements spec, how can you check this at compile time?

var _ Spec = (*MyType)(nil)

This will error at compile time:

prog.go:23: cannot use (*MyType)(nil) (type *MyType) as type Spec in assignment:
    *MyType does not implement Spec (missing Method method)
 [process exited with non-zero status]

Q: *MyType means pointer to MyType.

golangci-lint

The standard linter is golangci-lint. You can install it with go get, however in my experience, the runs tend to be inconsistent across different machines with this pattern. Likely due to caching.

I recommend using the docker container instead.

docker run --rm -v $(pwd):/app -v ~/.cache/golangci-lint/v1.64.8:/root/.cache -w /app golangci/golangci-lint:v1.64.8 golangci-lint run -v --fix 2>&1

To clean the cache inside the container, invoke golangci-lint cache clean inside the container, wiping everything in /root/.cache:

docker run --rm \
  -v ~/.cache/golangci-lint/v1.64.8:/root/.cache \
  golangci/golangci-lint:v1.64.8 \
  golangci-lint cache clean

Golang: for loop can be modernized using range over int

For loops can range over integers as of Go 1.22

// Normal way
for i := 0; i < 42069; i++ {
  // ...
}

// Range over int
for i := range 42069 {}

// And if you're not using i, go even simpler
for range 42069 {}

Bytes in Go

Debugging

go mod tidy

Opens permissions for the go directories that were unable to see the staandard library dependencies.

chown -R realu:realu $GOPATH
go clean -modcache

Testing

Testify assert vs. require

In the stretch/testify package, the two packages “assert” and “require” can both be used to give a flavor of assert statements.

import (
  "github.com/stretchr/testify/assert"
  "github.com/stretchr/testify/require"
)

Q: When is it better to use require versus assert?

It depends on the behavior you need. You get a log entry for each assertion with assert. However with require, the first failed requirement interrups and fails the complete test. This means that any requirements after the first failed one won’t be evaluated; they’ll be skipped.

Q: Test all go files visible from the current path.

go test ./...

To test with code coverage:

go test ./... -cover

Q: What does the -race flag do?

This enables the race detector when running tests. The race detector monitors memory access at runtime to check for race conditions where goroutines access the same variable concurrently, and at least one of them accesses a write operation.

When a race condition is detected, the output looks like:

==================
WARNING: DATA RACE
Read at 0x00c0000a0f70 by goroutine 7:
  main.main.func1()
      /path/to/main.go:14 +0x64

Previous write at 0x00c0000a0f70 by main goroutine:
  main.main()
      /path/to/main.go:16 +0x84

Goroutine 7 (running) created at:
  main.main()
      /path/to/main.go:13 +0x5c
==================
    Share:
    Back to Blog