Home

Awesome

The Go Programming Language

Notes, examples, and exercises from the awesome "The Go Programming Langauge" book.

Remember

Why Go?

Even if a lot of people complain about Golang, this is still the best choice for me. None of the issues they complain about are major issues for me (well, except the poor debug support and no generics). And it is the only language with the combination of things I like:

General

Names

Packages

Variables & Constants

var i int // Normal declaration
var p1, p2 *complex128 // Multiple variables of same type
var name, age = "Neo", 30 // Implicit declaration and initialization of different types
p3, x := &i, 12 // Short variable declaration of different types
chan := make(chan interface{}) // Notes about new() and make() below
var ( // Just like import statements, var declarations can be put in blocks
  x = isItVisible() // Initialization through function call
  y int // Zero value 0
  z = y + 1 // Initialization through expression
)
var v1 = Vertex{1,2} // Struct literal that declares and initializes a struct type defined as "type Vertex struct { x,y int }"
var v2 = Vertex{x: 1} // y gets initializes with its zero value, 0
os.Setenv("FOO", "1")
fmt.Println("FOO has value ", os.Getenv("FOO"))
fmt.Println(os.Args) // [./app-name a b c]
x := new(StructA) // x has type *StructA
v []int = make([]int, 100) // creates slice v, a struct that has non-zero fields: pointer to an array, length, and capacity. So, v is immediately usable
p *[]int = new([]int) // *p has zero value nil, which would make p useless in its current state
const ( // iota reset to 0
  c0 = iota // c0 == 0
  c1 = iota // c1 == 1
)
const ( // iota reset to 0
  f0 = 1 << iota // f0 == 1. Useful for flag constants
  f1 = 1 << iota // f1 == 2, f2 == 4, and so on
)
const x = iota // x == 0 as iota reset to 0

Functions

func add(x, y int) (sum int) {
  sum = x + y
  return // Will return the value of sum variable
}
func highOrderFunction(y int) func(int) int { // This high order function takes y int as parameter and returns an anonymous function that takes an int and returns an int. In this case, the returned value is a closure (not just an anonymous function) because it needs to save the value of y in it.
  return func(x int) int {
    return x + y
  }
}
var closure1 = highOrderFunction(1) // closure1(1) = 2 because this closure has saved a value of 1 for y. TODO: Verify that this is true. That the variable closure1 is actually a closure structure. Because printing the type of this variable currently shows "= func(int) int" as if it's just an anonymous function.
var closure2 = highOrderFunction(10) // closure2(1) = 11 because this closure has saved a value of 10 for y
func (s *MyStruct) pointerMethod() { } // method on pointer. Useful if struct needs to be modified, or if struct is too big to pass by value. Can't call this method with value because caller won't be expecting value to change
func (s MyStruct) valueMethod() { } // method on value. Can call this with pointer or value, but it will take value in either case. It's better to use value methods for data safety.
// In both cases, the methods are called similarly: s.pointerMethod() and s.valueMethod()
defer func() {
  if r := recover(); r != nil {
    fmt.Println("This prints")
  }
  fmt.Println("This prints too")
}()
panic("Don't know what to do")
fmt.Println("Never see this")

Loops

for { /* statements */ } // Zero components (infinite loop)
for i < 5 { /* statements */ } // Just a condition component (while loop)
for i = 0; i < 5; i++ { /* statements */ } // All components (initialization, condition, post)
for i, v := range "abc" { /* statements */ } // Range of values (from data types like string, array, slice, etc) to iterate over. Remember that v is passed by value, not by reference.
L: // Label useful to break out of multiple loops
  for {
    for {
      break L
    }
  }

Conditions

if v := getValue(); v < 5 {
  w := 2
} else {
  // v will be accessible here too. But not w
}
switch x := 2; x {
case 2: // First case that's checked
  // do something
case f(): // function is called and this case body is executed if x matches the value the function returned. Note: Just like f() here, x in the switch statement could also be a function like x(). In that case, the function is executed and the returned value is compared to cases.
  // do something
default: // default can be placed before other cases. It's evaluated ONLY if no case matches (including cases specified after the default)
  // do something else
}
x, y := 2, 3
switch {
case x == 2:
  fmt.Printf("x is 2")
case y == 3:
  fmt.Printf("y is 3") // This won't get executed because x == 2 case was matched already
case someFunc()
  fmt.Printf("someFunc() returned true")
default:
  fmt.Printf("Default")
}

Interfaces

type Point interface {
  Printer
  X() float64
  Y() float64
}
type Printer interface {
  Print()
}

Pointers

Slices, Arrays, and Lists

var arr [4]int // Declaration of array
x, y, z := [...]int{1,2}, [...]int{1,2,3}, [...]int{4,5,6} // Implicit declaration and initialization of multiple arrays.
x = y // will throw an error because the array types for x & y aren't same.
z = y // will work because the types are same.
s := []int{1,2} // Slice
a := [...]int{1,2} // Array
p := Point{5,5} // Struct Point with x and y fields
var aSlice []int{1,2} // Will create a slice and an underlying array
firstHalfSlice, secondHalfSlice, fullSlice := array1[:50], array1[50:], array1[:] // Say, array1 is a [100]int array
fmt.Printf("%T vs %T",fullSlice, array1) // will show "[]int vs [100]int"
s0 := make([]int, 2, 3) // [0,0] of array [0,0,0]
s1 := append(s0, 2) // [0,0,2] of array [0,0,2]
s2 := append(s0, 3) // [0,0,3] of array [0,0,3] Note: s1 is now [0,0,3] because the underlying array for s0,s1,s2 is the same
s3 := append(s0, 4, 4) // [0,0,4,4] of a new array [0,0,4,4] because old array wasn't big enough. s0,s1,s2 still use the same old array
s := [][]int{[]int{1, 2}, []int{3, 4}} // [ [1, 2], [3, 4] ]
l := list.New()
l.PushBack(32)
l.PushBack("str")

Strings

s1 := "A string"
s2 :=  "A " + "string"
s1 == s2  // will return true although &s1 != &s2.
for i := 0 ; i<len(str) ; i++ { // Iterating over bytes
  fmt.Printf("%d ", str[i])
} // we get 97 231 154 132 99. Only 97 and 99 are correct Unicode decimal codes. The middle rune took 3 bytes.
for _,v := range str { // Iterate over runes using range
  fmt.Printf("%d ", v)
} // we get 97 30340 99. All three Unicode decimal codes are correct. This is because the range iteration on a string is done on runes, not bytes.
regexp.MustCompile(`\..*`) // is easier than
regexp.MustCompile("\\..*")

Maps

a := make(map[int] string)

Goroutines

go someFunc() // Call someFunc in a goroutine
a = b
use(b)
// can be reordered by complier as:
use(b)
a = b
var lock sync.Mutex
lock.Lock()
// Do whatever
lock.Unlock() // Unlocked is the zero value of a mutex

HTTP HandleFunc