Adam Richardson's Site

Go Notes

Table of Contents

<2024-02-19 Mon>

Setup

Getting the latest release

  • Digital Ocean: How to install Go on Debian 10
  • The latest release of Go can be downloaded here
  • Copy the sha256 from the website to a local text file, echo "SHA_256_FROM_WEBSITE" > go_release_sha256.txt
  • Calculate the shaa256 of the download and ensure it matches the value in the text file made above
    • sha256sum go<VERSION>.linux-amd64.tar.gz
  • Extract the tarball with, tar -xvf go<VERSION>.linux-amd64.tar.gz
  • Change ownership of the go directory to the root user, sudo chown -R root:root ./go
  • Move the go folder to /usr/local, sudo mv go /usr/local
  • Update your path to include /usr/local/go/bin
    • Typically you will set GOROOT=/usr/local/go then update the path to have $GOROOT/bin

GOPATH

  • The default value for the $GOPATH environment variable is $HOME/go
  • Tools that are installed will be in $GOPATH/bin folder
  • It might be advantageous to add $GOPATH/bin to your $PATH environment variable

Hello World

  • The below code sample is a simple hello world program written in Go
  • It should go into its own project folder named hello-go and be named main.go
package main

import "fmt"

func main() {
        fmt.Printf("Hello, Go!\n")
}
  • To test out the Go install run go install hello-go
  • This should put a binary named hello-go in your path that should output "Hello, Go!" when run

Installing Beta Versions of Go

  • If you have go installed you can install a beta version using the go tool
  • For example: go install golang.org/dl/go1.18beta2@latest
  • Before you can run the beta version you need to run go1.18beta2 download
    • This will download the SDK into the $HOME/sdk folder
  • This will now make the beta available on your system as go1.18beta2. Use this as you would the go tool

Language Server

  • gopls is the official Go language server
  • You can install it with, go install golang.org/x/tools/gopls@latest
  • As far as I can tell the language server does not work in org babel source blocks

Emacs Org Babel Support

  • Emacs Package: ob-go
  • Manually edit org-babel-load-languages to include Go by adding it to the hash table (go . t)
  • This is an example using the args header argument, :args '("-count=5" "-msg=Hola")

Appending to a list

package main

import "fmt"

func main() {
        x := []int{}
        x = append(x, 10)
        fmt.Println(x)
}

Generics

Things you can do with generics that you cannot do with interfaces

  • The interface type only lets you define methods
  • This means that if you have a set of structs that all have a similar property you would need to define a method to access it generically
  • Using type constraints with structs allows you to define a generic type this is the union of all the other types

Simple Example

Non Generic Sum Functions

  • These sum functions only accept variables of a fixed type
  • These are here to contrast with the generic versions that can accept mulitple types
  • Note that the implementation of these functions is the exact same with the only difference being the type
func SumInts(m map[string]int64) int64 {
        var s int64
        for _, v := range m {
                s += v
        }
        return s
}

func SumFloats(m map[string]float64) float64 {
        var s float64
        for _, v := range m {
                s += v
        }

        return s
}

Generic Sum Function

  • In the square brackets below K and V are the names of the types used
  • comparable is a built in generic type that covers all types that support == and !=
  • Go requires map keys to be comparable
  • The V type is the union of int64 and float64, meaning only those types are allowed
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
        var s V
        for _, v := range m {
                s += v
        }

        return s
}

Generics with Type Constraint

  • You can create a named type constraint using an interface
type Number interface {
        int64 | float64
}

func SumNumbers[K comparable, V Number](m map[K]V) V {
        var s V
        for _, v := range m {
                s += v
        }

        return s
}

Main

  • When calling a generic function you can specify the types being used in square brackets
  • For example, SumIntsOrFloats[string, int64](ints) is clearly stating the types used for the key and value
  • The square brackets can be omitted when the compiler is able to infer the types like the below example
func main() {
        ints := map[string]int64{
                "first": 34,
                "second": 12,
        }

        floats := map[string]float64{
                "first": 35.98,
                "second": 26.99,
        }

        fmt.Printf("Non-Generic Sums: %v and %v\n",
                SumInts(ints), SumFloats(floats))

        fmt.Printf("Generic Sums: %v and %v\n",
                SumIntsOrFloats(ints),
                SumIntsOrFloats(floats))

        fmt.Printf("Generic Sums with Constraint: %v and %v\n",
                SumNumbers(ints),
                SumNumbers(floats))
}

Putting it all together

package main

import "fmt"

func SumInts(m map[string]int64) int64 {
        var s int64
        for _, v := range m {
                s += v
        }
        return s
}

func SumFloats(m map[string]float64) float64 {
        var s float64
        for _, v := range m {
                s += v
        }

        return s
}

func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
        var s V
        for _, v := range m {
                s += v
        }

        return s
}

type Number interface {
        int64 | float64
}

func SumNumbers[K comparable, V Number](m map[K]V) V {
        var s V
        for _, v := range m {
                s += v
        }

        return s
}

func main() {
        ints := map[string]int64{
                "first": 34,
                "second": 12,
        }

        floats := map[string]float64{
                "first": 35.98,
                "second": 26.99,
        }

        fmt.Printf("Non-Generic Sums: %v and %v\n",
                SumInts(ints), SumFloats(floats))

        fmt.Printf("Generic Sums: %v and %v\n",
                SumIntsOrFloats(ints),
                SumIntsOrFloats(floats))

        fmt.Printf("Generic Sums with Constraint: %v and %v\n",
                SumNumbers(ints),
                SumNumbers(floats))
}

Using flag package

package main

import (
        "flag"
        "fmt"
)

func main() {
        var msg string
        var count int64

        flag.StringVar(&msg, "msg", "hello world", "message to show the user")
        flag.Int64Var(&count, "count", 1, "number of times to show the message")

        flag.Parse()

        for i := 0; i < int(count); i++ {
                fmt.Println(msg)
        }
}

Bouncing DVD Logo w/ go-sdl2

This is a simple example that shows using go-sdl2 to render a bouncing DVD logo. This assumes that there is a ttf font in the root of the module named "Hack-Regular.ttf"

package main

import (
        "fmt"
        "os"

        "github.com/veandco/go-sdl2/sdl"
        "github.com/veandco/go-sdl2/ttf"
)

const (
        width    = 640
        height   = 480
        size     = 100
        fontPath = "Hack-Regular.ttf"
        fontSize = 48
)

func main() {
        if err := sdl.Init(sdl.INIT_EVERYTHING); err != nil {
                fmt.Fprintf(os.Stderr, "%s\n", err)
                os.Exit(1)
        }
        defer sdl.Quit()

        if err := ttf.Init(); err != nil {
                fmt.Fprintf(os.Stderr, "%s\n", err)
                os.Exit(1)
        }
        defer ttf.Quit()

        window, err := sdl.CreateWindow(
                "DVD Logo",
                sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED,
                width, height,
                sdl.WINDOW_SHOWN|sdl.WINDOW_ALLOW_HIGHDPI|sdl.WINDOW_RESIZABLE)

        if err != nil {
                fmt.Fprintf(os.Stderr, "%s\n", err)
                os.Exit(1)
        }
        defer window.Destroy()

        window.SetMinimumSize(width, height)

        renderer, err := sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED)
        if err != nil {
                fmt.Fprintf(os.Stderr, "%s\n", err)
                os.Exit(1)
        }
        defer renderer.Destroy()

        renderer.SetLogicalSize(width, height)
        renderer.SetIntegerScale(true)

        font, err := ttf.OpenFont(fontPath, fontSize)
        if err != nil {
                fmt.Fprintf(os.Stderr, "%s\n", err)
                os.Exit(1)

        }
        defer font.Close()

        running := true
        var x int32 = 73
        xDir := 1
        var y int32 = 92
        yDir := 1

        for running {
                for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
                        switch event.(type) {
                        case *sdl.QuitEvent:
                                running = false
                        }
                }

                x += int32(xDir)
                y += int32(yDir)

                if x > (width-size) || x < 0 {
                        xDir *= -1
                }

                if y > (height-size) || y < 0 {
                        yDir *= -1
                }

                renderer.SetDrawColor(0, 0x19, 0, 255)
                renderer.Clear()

                renderer.SetDrawColor(255, 255, 255, 255)
                rect := sdl.Rect{X: x, Y: y, W: size, H: size}
                renderer.FillRect(&rect)

                t, err := font.RenderUTF8Blended("DVD",
                        sdl.Color{R: 0, G: 0, B: 0, A: 255})
                if err != nil {
                        fmt.Fprintf(os.Stderr, "%s\n", err)
                        os.Exit(1)
                }
                defer t.Free()

                tex, err := renderer.CreateTextureFromSurface(t)
                if err != nil {
                        fmt.Fprintf(os.Stderr, "%s\n", err)
                        os.Exit(1)
                }
                defer tex.Destroy()

                err = renderer.Copy(
                        tex, nil,
                        &sdl.Rect{
                                X: x + (size-t.W)/2, Y: y + (size-t.H)/2,
                                W: t.W, H: t.H})
                if err != nil {
                        fmt.Fprintf(os.Stderr, "%s\n", err)
                        os.Exit(1)
                }

                renderer.Present()
                sdl.Delay(16)
        }
}

Recommended Packages

  • tarm/serial - Great cross platform serial communication library