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 As of the time of writing, that is Go 1.15. some common data structures like sets. There are two main reasons for that:
- +Go does not have generics2 Go indeed has generics starting with 1.18.
- 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 3 Go indeed has generics starting with 1.18. The usual structure for a
type maps 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