Notes taken from the go.dev documentation:
Primitive Types
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32
// represents a Unicode code point
float32 float64
complex64 complex128
The
int
,uint
, anduintptr
types are usually 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems.
Zero Values
The zero value is:
0
for numeric types,false
for the boolean type, and""
(the empty string) for strings.
Type Conversion
Go assignment between items of different type requires an explicit conversion:
i := 42
f := float64(i)
u := uint(f)
Constants
const Pi = 3.14
Loops
Only one loop in go.
sum := 1
// Conventional for:
for i := 0; i < 20; i++ {
sum += i
}
// init and increment not required:
for ; sum < 1000; {
sum += sum
}
// while-like loop
for sum < 1000 {
sum += sum
}
// loop forever
for {
}
Conditions
// Simple Condition
isRequired:= true
if isRequired {
}
// Can have a scoped initializer
if v := math.Pow(x, n); v < lim {
return v
}
// Initializer also available within else blocks
if v := math.Pow(x, n); v < lim {
return v
} else {
fmt.Printf("%g >= %g\n", v, lim)
}
Switches
Switches do not fall through in go. No
break
s required.
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.\n", os)
}
Defer
Defers until the surrounding returns.
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
// LIFO
fmt.Println("counting")
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
Pointers
&varName
: Address Operator for a value.*pointerName
Value operator of a pointer.var pointer *Type
to create a pointer with an zero value ofnil
;
i, j := 42, 2701
p := &i // point to i
fmt.Println(*p) // read i through the pointer
*p = 21 // set i through the pointer
fmt.Println(i) // see the new value of i
p = &j // point to j
*p = *p / 37 // divide j through the pointer
fmt.Println(j) // see the new value of j
Structs
- Collection of fields.
type Vertex struct {
I int
S string
}
fmt.Println(Vertex{1, "Hello" })
// order not required
fmt.Println(Vertex{I: 1, S: "Hello"})
Arrays
- Required type & size.
- Slices are dynamic views into arrays using [startIndex:endIndex] — Last index not included.
- Changing a slice changes the underlying array too.
- For a slice capacity starts from the start index of the slice to the end index of the original array.
var a [2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1])
fmt.Println(a)
primes := [6]int{2, 3, 5, 7, 11, 13}
fmt.Println(primes)
// Slices are dynamic views into arrays using [startIndex:endIndex] -- Last index not included. Change
v int[] = primes[1:3]
s := []int{2, 3, 5, 7, 11, 13}
s = s[1:4]
fmt.Println(s)
s = s[:2]
fmt.Println(s)
s = s[1:]
fmt.Println(s)
Methods
- Functions with receiver arguments.
- Allow to bind functions to types.
- Reasons for pointer receiver vs value receiver:
- The first is so that the method can modify the value that its receiver points to.
- The second is to avoid copying the value on each method call. Performance.
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(v.Abs())
}
Interfaces
- Type of method signatures.
- Do not need to be explicitly implemented.
type Interface interface {
Method()
}
type Struct struct {
String string
}
func (s Struct) Method() {
fmt.Println(s.String)
}
func main() {
var i Interface = Struct{"hello"}
i.Method()
}
// Interface i type T assertion
t := i.(T)
Errors
type error interface {
Error() string
}
Generics
- Similar to TypeScript.
- Use
comparable
to allow comparison b/w values.
// Index returns the index of x in s, or -1 if not found.
func Index[T comparable](s []T, x T) int {
for i, v := range s {
// v and x are type T, which has the comparable
// constraint, so we can use == here.
if v == x {
return i
}
}
return -1
}
func main() {
// Index works on a slice of ints
si := []int{10, 20, 15, -10}
fmt.Println(Index(si, 15))
// Index also works on a slice of strings
ss := []string{"foo", "bar", "baz"}
fmt.Println(Index(ss, "hello"))
}
Go Routines
- Lightweight threads in go.
- Started using
go f(x, y, z)
- Use channel to communicate data between go routines.
- Channels are FIFO.
- Only the sender should close a channel, never the receiver. Sending on a closed channel will cause a panic.
- Channel Types:
- Unbuffered: Are synchronous, Sender & receiver need to be ready at the same time.
- Buffered: Asynchronous, Blocks can remain in channels until the buffer gets filled. After than a block needs to be read before new block can be sent.
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
// Advanced: Buffered Channels
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
Select
- Like switch but blocks until one of the conditions is true.
- If default is added it goes not block and goes to default.
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println(" .")
time.Sleep(50 * time.Millisecond)
}
Mutex
- Go’s standard library provides mutual exclusion with
sync.Mutex
and its two methods:Lock
Unlock
package main
import (
"fmt"
"sync"
"time"
)
// SafeCounter is safe to use concurrently.
type SafeCounter struct {
mu sync.Mutex
v map[string]int
}
// Inc increments the counter for the given key.
func (c *SafeCounter) Inc(key string) {
c.mu.Lock()
// Lock so only one goroutine at a time can access the map c.v.
c.v[key]++
c.mu.Unlock()
}
// Value returns the current value of the counter for the given key.
func (c *SafeCounter) Value(key string) int {
c.mu.Lock()
// Lock so only one goroutine at a time can access the map c.v.
defer c.mu.Unlock()
return c.v[key]
}
func main() {
c := SafeCounter{v: make(map[string]int)}
for i := 0; i < 1000; i++ {
go c.Inc("somekey")
}
time.Sleep(time.Second)
fmt.Println(c.Value("somekey"))
}