Official walkthrough: Tour of Go
- think of a channel as a way of creating a stream IO between two concurrent subroutines
- Go race detector
- locking with WaitGroup
- Deferred call execution order: last-in-first-out
- It is common to write methods that gracefully handle being called with a nil receiver.
https://tour.golang.org/methods/23
-
variadic functions are kinda sorta like arg splats
-
Reasoning for multiple adjacent
defer()
s: ensure that all subroutines withindefer()
s are attempted in the case that a panic occurs within one of thedefer()
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
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:
- So that a method can modify the value its receiver points to
- To avoid copying the value on each function call
All methods on a value should have either value or pointer receivers, but not both.
- 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
-
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()
- 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)
- 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"
-
Attribute export is conditioned on name case:
obj.Public
,obj.private
- recursively walk a dir on os
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
t.Log(path)
return err
})
fmt.Stringer
interface'sString
methoderror
interface'sError
methodio.Reader
interface'sRead
method
- 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 notGOCACHE=false
)
- "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)
}