Go
Some notes regarding the Go language. Some topics have graduated to their own page:
Setup
First things first. How to install the Go language in different OSes.
Fedora
You can either use dnf
directly and simply run
$ sudo dnf install golang-bin
This might not install the latest and greatest. If you want to use the most recent version, download it directly from https://golang.org/doc/install#install.
If applicable, delete any previous /usr/local/go
directory with
$ sudo rm -rf /usr/local/go
Next, extract the archive file with
$ sudo tar -C /usr/local -xzf /home/foo/tmp/go-$VERSION.linux-amd64.tar.gz
And add /usr/local/go/bin
to the $PATH
.
macOS
The best way to perform an attended installation of Go in macOS is to simply download the installer from https://go.dev/dl/ and running it.
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:
- +Go does not have generics2
- 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 2
The usual structure for a type map
s is quite straight-forward.T
is map[T]bool
, where the key is the element and the value is just a placeholder. For instance, for a int
set, we can add elements:
s := map[int]bool{1: true, 3: true}
s[1] = true // already presenvar t
s[2] = true // adds new element
Some other techniques for maps replacing sets:
Set union
set_1 := map[int]bool{1:true, 2:true, 3:false}
set_2 := map[int]bool{1:false, 2:false, 4:false}
set_union := map[int]bool{}
for k, _ := range set_1{
set_union[k] = true
}
for k, _ := range set_2{
set_union[k] = true
}
fmt.Println(set_union)
Set intersection
set_1 := map[int]bool{1:true, 2:true, 3:false}
set_2 := map[int]bool{1:false, 2:false, 4:false}
set_intersection := map[int]bool{}
for k,_ := range set_1 {
if set_2[k] {
set_intersection[k] = true
}
}
fmt.Println(set_intersection)
To convert a (map) set to an array:
array := make([]int, 0)
set_1 := map[int]bool{1:true, 2:true, 3:false}
for k := range set_1 {
array = append(array, k)
}
fmt.Println(array)
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 Testing in 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?
The conversion is simple:
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