Skip to content

Go

Packaging

  • File starts with package <name>.
  • A name is exported if it begins with a capital letter.
go
package main

import (
  "fmt"
  "math"
)

...

Functions

  • func keyword.
  • Omit shared type in params.
  • Multiple return values.
  • Named return values.
  • Rest params looks like TypeScript.

Declarations

  • No colon between name and type.
  • May omit shared type.
  • Read left to right, compared to C's read in a spiral.
go
var i, j int = 1, 2
x, y := true, 1
var (
  u bool = true
  v int  = 1
)

Types

Basic Types

go
bool
string
int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64
complex64 complex128
uintptr // raw pointer
byte    // alias for uint8
rune    // alias for int32, represents a Unicode code point
  • "Zero value" as default.
  • Explicit type conversion: float64(i)
  • Numeric constants are high-precision and can be used as any numeric type.

Pointer Types

go
*T
  • nil as default
  • &v and *p to ref and deref
  • Implicit deref for struct member access.
  • No pointer arithmetic (like reference?)

Interface Types

go
type any    = interface {}
type error    interface { Error() string }

It seems that in history any was added in 1.18 as an alias of interface {}, that's why it is a newtype instead of an interface declaration.

Composite Types

go
[n]T
[]T
map[K]V
chan T, chan<- T, <-chan T
struct { ... }
func(A1, A2) R
  • Array, slice and map are operated by a set of builtin methods:
    make, len, cap, append, delete, ...

Newtype

go
type MyFloat float64

Control Statements

  • No parens, like Rust.
  • All loops are for:
go
// Conventional for loop
for i := 0; i < 10; i++ {
  ...
}

// while loop
for sum < 1000 {
  ...
}

// forever loop
for {
  ...
}

// Range loop
for i, v := range arr {
  ...
}
  • If (and switch) can have a short statement:
go
if v := math.Pow(x, n); v < lim {
  return v
} else {
  fmt.Printf("%g >= %g\n", v, lim)
}
  • Switch works like JS: case values can be expressions.

Defer

  • Not executed until the surrounding function returns.
  • Stacked: last in first out.

TODO: https://golang.google.cn/blog/defer-panic-and-recover

Struct

  • Constructed by order/name
  • Implicit default value for each field
go
type Vertex struct {
  X, Y int
}
var (
  v1 = Vertex{1, 2}
  v2 = Vertex{X: 1}  // Y:0 is implicit
)

Methods

  • Function with a receiver argument, which may be a pointer.
  • Can only declare methods for types defined in the same package.
    → May require a newtype.
  • Method's responsibility to handle nil value.
go
func (v *Vertex) Scale(f float64) {
  v.X = v.X * f
  v.Y = v.Y * f
}

Interfaces

  • Interfaces are implemented implicitly. (e.g. any)
  • Interface value can be considered as (value, type):
go
type I interface {
  Stringer        // Interface embedding
  Value() int     // Method set
  ~int | ~string  // Type set
}

func describe(i I) {
  fmt.Printf("(%v, %T)\n", i, i)
}

Type Assertions

go
func main() {
  var i any = "hello"
  s, ok := i.(int)
  s := i.(int) // Panic
}

Type Switches

go
func do(i any) {
  switch v := i.(type) {
    case int:    fmt.Println(v * 2)
    case string: fmt.Println(len(v))
    default:     fmt.Printf("%T\n", v)
  }
}

It seems that .(type) can only be used in a switch.

Type parameters

  • Explicit constraint is required.
  • No HKT.
go
func Index[T comparable, ](s []T, x T) int {
  for i, v := range s {
    if v == x {
      return i
    }
  }
  return -1
}

type List[T any] struct {
  next *List[T]
  val  T
}

func main() {
  var f func(s []int, x int) int
  f = Index[int]
}

Goroutine

  • Lightweight: Multiple goroutines on OS threads. Go's runtime handles their scheduling.
  • Launch anywhere: No function coloring.
  • Channel as builtin.
go
func sum(s []int, c chan int) {
	sum := ...
	c <- sum // send sum to c
}

func main() {
	s := []int{...}

	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)
}

select channel

  • Race for the ready case.
  • May have a default case to make it non-blocking.
go
func main() {
	start := time.Now()
	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("wait.")
			time.Sleep(50 * time.Millisecond)
		}
	}
}

TODO: How goroutine is implemented.

meow

TODO.

References