Study Guide 2023+

go

Warning: These notes are partial, ongoing, incomplete, and may contain typos/inaccuracies. (They are kept factually accurate, time permitting.)

They are being united from many disparate notes created in the past and the layout/organization will gradually improve with time!

Please view them on a computer as they are not optimized for mobile (although you can still view them on Mobile along with the Flashcards at your own risk)!

Topics and code examples are lazy-loaded and may require two-clicks from the TOC to correctly calculate the updated x,y coordinates (after rendering). Thanks!

Go: General Concepts

  1. Go is a Strongly Typed, Compiled, programming language that supports essential Object Oriented concepts without imposing a rigid, heavy-weight, OOD hierarchy.

  2. The Golang Empty Interface can serve as a kind of Generic:

    // GoLang generic
    // The empty interface specifies that any type may be passed
    func golangGeneric(val interface{}) {
      fmt.Println(val)
    }
    
  3. Export from files and Modules using capitalized Variable Names.

  4. Use make() for Arrays and Maps:

    nums := [][]int{{1,1},{0,0},{2,1},{100,200},{15,244}}
    a := make([]int, 5)
    
    var animals = make(map[string]string)
    
  5. Go has no Ternary Operator.

  6. Variables can be declared in two ways:

    • var:

      • Can be used in Global Scope (at the top of a Module/Package).
      • Optional declared Type.
      var a = 900
      
    • :=

      • Short Variable Declaration
      • Can only be declared in some local Scope.
      b := 100
      

Go: Object Oriented Design

Go uses Structs instead of Classes.

They are most akin to a kind of mutable Java Record or JavaScript Object IMO.

They lack internal Functions ("true Methods") and Constructors like in many other languages.

Inheritance

Go does not have pure Inheritance. It supports Weak Composition and Embedding:

type Animal struct {
    name string
}

// Weak Inheritance by Composition
type Mammal struct {
    Animal
}

// As a field
type Reptile struct {
    animalParts Animal
}

Polymorphism

Polymorphism is achieved through flexible Structs where an attribute plays the role of a Type.

Receiver Functions

type Animal struct {
    animalName string
    foodEaten string
    locomotionMethod string
    spokenSound string
}

func (a Animal) Eat() {
    fmt.Println(a.foodEaten + "\n")
}

func (a Animal) Move() {
    fmt.Println(a.locomotionMethod + "\n")
}

func (a Animal) Speak() {
    fmt.Println(a.spokenSound + "\n")
}

Methods (Functions that live within a Class or Instance) are implemented by Receiver Function pattern.

Visibility

Encapsulation is obtained and Access controlled through several techniques and in-built properties of the language:

  1. Export from files and Modules using capitalized Variable Names. (Non-capitalized Variable Names will not be exported - are functionally private.)
  2. Fields on Structs can be read and altered using familiar dot-notation: p.X + p.Y + p.Z.

Examples

type Animal struct {
    animalName string
    foodEaten string
    locomotionMethod string
    spokenSound string
}

cow := Animal{"cow", "grass", "walk", "moo"}
bird := Animal{"bird", "worms", "fly", "peep"}
snake := Animal{"snake", "mice", "slither" ,"hsss"}
// Note this is a weakly OOP language
// Note the lack of inheritance and constructors

type Point struct {
    X    float32
    Y    float32
    Z    float32
}

// Pseudo constructor/factory
// Note capitalized name indicates "to export"
func NewPoint(X float32, Y float32, Z float32) *Point{

    // Partially initialize object
    p := Point{X:X, Y: Y}
    p.Z = Z

    // Return pointer
    return &p
}

// Pseudo-class methods
// Call by value
// Copy of value made

func AddCoordinates(p Point) float32 {
    return p.X + p.Y + p.Z
}

// Receiver type function
// This is also how visibility is controlled:
// By exporting receiver methods but limiting exporting of structs (by using lower-case names)

func (p Point) AdditionReceiver () float32 {
    return p.X + p.Y + p.Z
}

Code samples:

  1. https://github.com/Thoughtscript/go_refresh/blob/master/courses/9-structsreceivers/main.go
  2. https://github.com/Thoughtscript/languages_2024/blob/main/go/ood/struct.inheritance.go

Go: Asynchronous Programming

Go uses WaitGroups, go routines, and the defer keyowrd to make both Multi-Threaded and Asynchronous Programming possible.

func dine(wg *sync.WaitGroup, philosophers []*Philosopher) {
    defer wg.Done()
    //...
wg := new(sync.WaitGroup)

wg.Add(1)
go dine(wg, philosophers)
wg.Wait()

Channels

The output of a go routine can be sent to a Go Channel:

func sortAsync(wg *sync.WaitGroup, arr []int, c chan []int) {
    defer wg.Done()
    fmt.Printf("Go begin: %v \n", arr)
    for i := 0; i < len(arr) - 1; {
        if arr[i] > arr[i+1] {
            orig := arr[i]
            arr[i] = arr[i+1]
            arr[i+1] = orig
            i = 0
        } else {
            i++
        }
    }
    c <- arr
    fmt.Printf("Go end: %v \n", arr)
}
// Create necessary resources
wg := new(sync.WaitGroup)
c := make(chan []int)
arrs := make([][]int, 4)

//...

for i := 0; i < len(arrs); i++ {
    wg.Add(1)
    fmt.Printf("Sorting: %v \n", arrs[i])
    go sortAsync(wg, arrs[i], c)
}

sOne := <- c

Code samples:

  1. https://github.com/Thoughtscript/go_refresh/blob/master/courses/12-gosort/main.go
  2. https://github.com/Thoughtscript/go_refresh/blob/master/courses/13-philosophers/main.go

Go: Pointers

Like C++, Go gives explicit control over the use of Pointers.

Useful to control scenarios where we'd encounter: Deep/Shallow Copying, Pass by Value, and Pass by Reference.

Address Of Operator and Dereferencing

var num int = 100
var numAddress *int = &num // declare a variable with pointer type '*int'
                           // obtain the address of 'num'
var derefNum int = *numAddress  // dereference back to the int value from the pointer type
*numAddress = 42 // update the value of dereference to 'numAddress'

Examples

var num int = 100
fmt.Println("pointers > <variable> 'num' has <value>:", num)

var numAddress *int = &num
fmt.Println("pointers > 'numAddress' obtains <pointer> via & <address operator> and <pointer type>: *int = &num ", numAddress)

var derefNum int = *numAddress
fmt.Println("pointers > <derefences> back to the <value> with <dereferencing operator> on 'numAddress': derefNum = *numAddress", derefNum)
fmt.Println("pointers > call & <address operator> on 'derefNum' to obtain <address>: &derefNum", &derefNum)
fmt.Println("pointers > can <dereference> back directly with & and * operators sequentially: *&derefNum", *&derefNum)

*numAddress = 42
fmt.Println("pointers > set <value> on <dereference> of 'numAddress', then get <address> from <pointer type>: *numAddress = 42", numAddress)
fmt.Println("pointers > <dereference> back to <value>: *numAddress ", *numAddress)
pointers > <variable> 'num' has <value>: 100
pointers > 'numAddress' obtains <pointer> via & <address operator> and <pointer type>: *int = &num  0xc0000a4090
pointers > <derefences> back to the <value> with <dereferencing operator> on 'numAddress': derefNum = *numAddress 100
pointers > call & <address operator> on 'derefNum' to obtain <address>: &derefNum 0xc0000a4098
pointers > can <dereference> back directly with & and * operators sequentially: *&derefNum 100
pointers > set <value> on <dereference> of 'numAddress', then get <address> from <pointer type>: *numAddress = 42 0xc0000a4090
pointers > <dereference> back to <value>: *numAddress  42

Code samples:

  1. https://github.com/Thoughtscript/languages_2024/blob/main/go/pointers/pointers.go

Go: Generics

Examples

any Type:

func anyParamTypeOne[T any](x T) T {
    return x
}

Empty Interface:

// Everything implements this interface and so 
// can be passed successfully here
func golangGeneric(val interface{}) {
    fmt.Println(val)
}

With a shared interface that types are implementing:

func exampleNine[TT W](s TT, t TT) {
    fmt.Println("reflection > exampleNine", s, t)
}

Code samples:

  1. https://github.com/Thoughtscript/languages_2024/blob/main/go/reflection/reflection.go#L86C1-L88C2
  2. https://github.com/Thoughtscript/languages_2024/blob/main/go/generics/generics.go
  3. https://github.com/Thoughtscript/languages_2024/blob/main/go/generics/emptyinterface.go

Go: Interfaces

Examples

type S interface{}

type W interface {
    M()
}

type AA struct {
    msg string
}

type BB AA

type CC BB

func (p AA) M() {
    fmt.Println(1 + 2)
}

// Each of BB and CC require the implementation method M() 
// they do not automatically or implicitly inherit like super()
func (p BB) M() {
    fmt.Println(1 + 2)
}

func (p CC) M() {
    fmt.Println(1 + 2)
}
  1. Implicit - no explicit implementation keyword required (such as implements in Java).
  2. Above, AA, BB, CC implement both S and W.
  3. If BB, CC did not implement M() they'd fail to implement S despite being related to AA directly through their type definitions.
  4. M() isn't implicitly inherited (not equivalent to super() in other languages) here.
  5. S is effectively the Empty Interface since it lacks any methods to be implemented (and will even allow say var X string to implement S).
    • Therefore, use an interface definition like W for actual parameter or type constraints.

Code samples:

  1. https://github.com/Thoughtscript/languages_2024/blob/main/go/interfaces/interfaces.go
  2. https://github.com/Thoughtscript/languages_2024/blob/main/java/src/main/java/thoughtscript/io/review/InterfaceAggregationTest.java

Go: Reflection

Allows one to reflect and introspect the properties of a Variable, struct, or type.

Examples

type W interface {
    M()
}

var wtype = reflect.TypeOf((*W)(nil)).Elem() // Obtain the type of interface W

func exampleEight(s AA, t any) {
    T := reflect.TypeOf(t)                    // Get the type of argument t
    if T.Implements(wtype) {                  // Ensure that T implements interface W
        fmt.Println("reflection > exampleEight", s, t)
    } else {
        panic("reflection > t doesn't implement W")
    }
}

Code samples:

  1. https://github.com/Thoughtscript/languages_2024/blob/main/go/reflection/reflection.go