The Go language

Some notes regarding the Go language. Some topics have graduated to their own page:

Language design

Go doesn't have sets

The Go language, notoriously, does not have1 some common data structures like sets. There are two main reasons for that:

  1. Go does not have generics1
  2. Go relies on you writing your own data structures, generally

Go lacks generics, which prevent writing a ... well, generic and efficient set implementation. Also, writing your own (non-generic) set with maps is quite straight-forward. The usual structure for a type T is map[T]bool, where the key is the element and the value is just a placeholder. For instance, for a int set:

s := map[int]bool{1: true, 3: true}

where we can add elements:

s[1] = true // already present
s[2] = true // adds new element

Some other techniques for maps replacing sets:

Set union

set_union := map[int]bool{}

for k, _ := range set_1{
    set_union[k] = true
}
for k, _ := range set_2{
    set_union[k] = true
}

Set intersection

set_intersection := map[int]bool{}
for k,_ := range set_1 { 
  if set_2[k] {
    set_intersection[k] = true
  }
} 

Set to array

To convert a (map) set to an array:

array := make([]int, 0)

for k := range set_1 {  
   array = append(array, k)  
}

CI

GitHub

A potential workflow for GitHub is to use GitHub Actions for Go. An example workflow file, .github/workflows/test.yml, which runs go test (see Go) and go vet is:

on: [push, pull_request\]
name: Test
jobs:
  test:
    strategy:
      matrix:
        go-version: [1.14.x, 1.15.x]
        os: [ubuntu-latest]
    runs-on: ${{ matrix.os }}
    steps:
    - name: Install Go
      uses: actions/setup-go@v2
      with:
        go-version: ${{ matrix.go-version }}
    - name: Checkout code
      uses: actions/checkout@v2
    - name: Test
      run: go test ./...
    - name: Vet
      run: go vet ./...

Containers

Minimal example

A minimal example of a Go container configuration for a web server running on port 8080:

# Start from the latest golang base image  
FROM golang:latest  
# Add Maintainer Info  
LABEL maintainer="Rui Vieira"  
# Set the Current Working Directory inside the container  
WORKDIR /app  
# Copy go mod and sum files  
COPY go.mod go.sum ./  
# Download all dependencies. Dependencies will be cached if the go.mod and go.sum files are not changed  
RUN go mod download  
# Copy the source from the current directory to the Working Directory inside the container  
COPY . .  
# Build the Go app  
RUN go build -o main .  
# Expose port 8080 to the outside world  
EXPOSE 8080  
# Command to run the executable  
CMD ["./main"]

Reference

Conversions

How to convert a string to byte array?

b := []byte("This is a string")

Collections

Sort map keys alphabetically

If a map contains string keys, i.e. var myMap map[string]T, we must sort the map keys independently. For instance:

keys := make([]string, 0)
for k, _ := range myMap {
    keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
    fmt.Println(k, myMap[k])
}

Check for element

If we consider a collection, say, []string collection, the way to check for an element already present is, for instance:

func existsIn(needle string, haystack []string) bool {
    for _, element := range haystack {
        if element == needle {
            return true
        }
    }
    return false
}

Templates

Check if variable empty

In a Go template you check if a variable is empty by doing:

{{if .Items}}
    <ul>
        {{range .Items}}
            <li>{{.Name}}</li>
        {{end}}
    </ul>
{{end}}

Looping over a map

Looping over the map var data map[string]bool in a Go template:

{{range $index, $element := .}}
    {{$index}}: {{$element}}
{{end}}

Processes

Executing external processes

Executing an external process and directing input and output to Stdout and Stderr.

cmd := exec.Command("ls", "-1ao")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
    log.Fatalf("cmd.Run() failed with %s\\n", err)
}

Testing in Go

Place the tests in your place of choosing, but keep the package declaration. Test functions should be parameterised as (t *testing.T and start with the prefix Test, for instance:

package main

func TestFoo(t *testing.T) { 
    value := Foo(5, 5) 
    // ... assertions

The test files themselves must have the suffix *_test.go. Call the tests with go test.

Output

Printing struct keys

To print a struct along with its keys, we can use the Printf switch as in the official documentation. That is,

fmt.Printf("%+v\n", myStruct)

Date and time

Check if a date is empty

If a date is unassigned, the .IsZero() method can be used to check it

a := time.Time{}
a.IsZero() // This will be true

  1. As of the time of writing, that is Go 1.15.