Skip to content

Instantly share code, notes, and snippets.

@rfong
Last active June 1, 2021 02:45
Show Gist options
  • Save rfong/cd604995558f1e481abb031aa46435f7 to your computer and use it in GitHub Desktop.
Save rfong/cd604995558f1e481abb031aa46435f7 to your computer and use it in GitHub Desktop.
golang onboarding notes

Official walkthrough: Tour of Go

Concurrency mindmap

  • think of a channel as a way of creating a stream IO between two concurrent subroutines
  • Go race detector
  • locking with WaitGroup

Goroutine trivia

Idiomatic usage & common patterns

https://tour.golang.org/methods/23

Syntax/semantics

  • variadic functions are kinda sorta like arg splats

  • Reasoning for multiple adjacent defer()s: ensure that all subroutines within defer()s are attempted in the case that a panic occurs within one of the defer()s.

  • map[int32]struct{} is sort of the Go way of doing a set -- map from something to nothing.

  • HOT TIP: to check "set" inclusion in one line without error-checking, you can map to boolean values that are always true, to take advantage of the default=false behavior.

mySet := make(map[string]bool)
mySet["foo"] = true
mySet["bar"] = true
fmt.Println(mySet["foo"]) // true
fmt.Println(mySet["goo"]) // false

Pointers

Given a struct pointer p, p.X can be used in place of (*p).X as implicit shorthand to dereference the pointer when accessing a struct field. Do not be fooled; p is still a pointer.

Pointer receivers: methods with non-pointer receivers may be called, but cannot modify the value to which the receiver points; they will instead operate on a copy of the receiver's value. Reasons to use them:

  1. So that a method can modify the value its receiver points to
  2. To avoid copying the value on each function call

All methods on a value should have either value or pointer receivers, but not both.

Pointer indirection:

  • functions with a pointer argument must take a pointer
  • functions with a value argument must take a value
  • methods with a pointer receiver may take either a value or pointer -- but remember that they behave differently
  • methods with a value receiver may take either a value or pointer

Polymorphism happy fun times

  • PLEASE DO NOT MOCK OBJECTS IN GO, YOU CANNOT OVERLOAD FUNCTIONS

  • calling an embedded struct method (analogous to Python's super but not exactly the same)

    type SubStruct struct {
      SuperStruct  // this is the embed
    }
    SubStructInstance = SubStruct{...}
    SubStructInstance.SuperStruct.method()
  • If the embedded struct is from another package (pkg.SuperStruct), the same syntax is used to call it.
    type SubStruct struct {
      pkg.SuperStruct  // this is the embed
    }
    SubStructInstance = SubStruct{...}
    // just don't use the package when you call it. YOUR NAMESPACE UNIQUENESS HAD BETTER BE ROCK SOLID
    SubStructInstance.SuperStruct.method()

Types

  • how to do a type switch on a map[string]interface{}
data := map[string]interface{}{
  "name": "Ben Bitdiddle",
  "age": 22,
  "pi": 3.14159,
}

if strVal, ok := data[attr].(string); ok {
  fmt.Println("string,", strVal)
}
if intVal, ok := data[attr].(int); ok {
  fmt.Println("int,", intVal)
}
if float64Val, ok := data[attr].(float64); ok {
  fmt.Println("float64,", float64Val)
}
  • The empty interface may hold values of any type, since every type implements at least zero methods. Empty interfaces are used to handle values of unknown type.

  • Type assertions provide access to an interface value's underlying concrete value. The syntax is similar to reading from a map: t, ok := myInterface.(T)

Syntax trivia

  • how to dump a stacktrace:
    import "runtime/debug"
    debug.PrintStack()
  • how to string format a pointer: "%p"

  • how to string format an object/field "%+v"

  • spew.Dump

  • Attribute export is conditioned on name case: obj.Public, obj.private

Standard lib trivia

  • recursively walk a dir on os
  filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
    t.Log(path)
    return err
  })

Commonly implemented interfaces

CLI tips

  • Run tests in the 'api' package that match 'TestSomeCase*' go test -v -run TestSomeCase ./api
  • Simple benchmark run: go test -test=^$ -bench=. ./retriever/engine
  • Run benchmark for longer: go test -bench=. -benchtime=60s
  • Run tests without caching: GOCACHE=off go test (SURPRISE, it's not GOCACHE=false)

Troubleshooting

  • "X does not implement Y (... method has a pointer receiver) This compile-time error arises when you try to assign or pass (or convert) a concrete type to an interface type; and the type itself does not implement the interface, only a pointer to the type.
  • Under the hood, interfaces have a value and a concrete type.
func describe(i interface{}) {
	fmt.Printf("(%v, %T)\n", i, i)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment