This is not a tutorial, and I’m not expecting anyone to learning Go by reading this because I’m a novice as well. This is somewhere I write down some notes as I learn.

Install Go

Installing Go is quite simple, which is nothing more than

# Download the binary and install to /usr/local/go

rm -rf /usr/local/go
wget -O- $TAR_URL | tar -C /usr/local -xvz
# Add these two lines to .bashrc

export PATH=$PATH:/usr/local/go/bin
export GOPATH=/home/ubuntu/go

What is the difference between GOPATH and GOROOT. Well, GOPATH is like your working dir, where you put third party packages or source codes. The following code installs testify package,

# install the package testify
go get

and you get this in GOPATH/pkg

└── linux_amd64
        └── stretchr
            └── testify.a

While GOROOT is like the path to your Golang software, which is needed if you have multiple versions of Go installed.

Something to remember…


Go programs are made up of packages. Program start running in package main. The following code imports fmt and math/rand to do a fairly simple job. The last element of package path can be directly used.

package main
import "fmt"
import "math/rand"
func main() {
    // use rand.Xxx directly

The import can also be

import (

Exported name start with capital letter.


// Define a function
func funcName(arg type, arg2 type2) (type3, type4) {
    return val1, val2

// Named return
// Don't use too much
// Unreadable
func add(a, b int) (x int) {
    x = a + b


package main

import "fmt"

var a, b, c int // package level

func main() {
    var i int // function level declaration
    var j int = 3 // with init value
    k := 8 // Implicite type
    const g = 12 // Constant
    const (
        Big = 1 << 100 // shift 1 100 left
        // Untyped numeric const takes the type needed by context

Basic types

T(x) // type conversion

Confrol flow

For loop

for init; end; inc {


for cond {
    // like while
for {
    // forever


switch var {
    case value1:
        do something
    case value2:
        do something else
        do default_ shit

Evalute from top to bottom, stop when a case succeeds.

// switch with no condition
switch {
    case cond1:
    case cond2:

When used with no condition, a full condition can be placed after case.


Defers the execution of a function until the surrounding function returns

func main() {
    defer fmt.Println("world")

    fmt.Println("hello, ")
// Get hello, world in console

Deferred fn are pushed to stack, so executed first-in-last-out order.


func incr(i *int) {
    i ++

i := 1
p := &i

// We get i=2 as a result


// struct is a collection of fields

type Vertex struct {
    X int
    Y int

v := Vertex{1,2}
v.X = 3
// Modify X to 3

p := &v
// Both are valid

v2 := Vertex{X:1}
// Y is 0 by default

Arrays & Slice

// Array of type T with lengh n
// Length is an attribute, which is immutable

var a [10]int
// a is an int array with length 10

Slice is dynamically sized, references to arrays.

// Slice of type T
c := [3]int{1,2,3}
a := []int{1,2,3}
cs := []int c[0:2] // [2,3]
nilSlice := nil // can be nil

makeslice := make([]int, 5)
matrix := [][]int {
} // 2d slice

append(a, 1) // append to slice
// array re-allocation happens

for i, v := range a {
    do sth to i and v

Slicing from the beginning does not affect cap, and you can always re-slice to extend. But slicing from the middle cut down cap, you cannot re-slice to extend back.


m := map[string]int
// map string to int

m = make(map[keyType]valType)
m[key] = val
delete(m,key) // delete a value

elem, ok = map["key"]


func adder() func(int) int {
    sum := 0
    return func(x int) int {
        sum += x
        return sum
// adder() returns a closure, each one with its own sum bounded

func main() {
	pos, neg := adder(), adder()
	for i := 0; i < 10; i++ {


type Vertex struct {
	X, Y float64

func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)

func (v *Vertex) Testp() int {
    v.X = 1
    return v.X
    // Pointer receiver
    // Modifies struct value

func main() {
    v := Vertex{3, 4}
    // Can either take pointer or value, interpreted as accepted by pointer


An interface type is defined as a set of method signatures. A value of interface can hold any value that implements those methods.

type Int_f interface {
    Incr() int

type Int struct {
    V int

func (i *Int) Incr() int {
    return i.V + 1

var a Int_f
v := Int{1}

a = v // Error, no implementation for Int
a = &v // Correct, pointer receiver is implemented

Interface values can be thought of as a tuple of a value and a concrete type

(value, type)

An interface value holds a value of specific underlying concrete type. Calling a method of interface value executes the method of same name on its underlying type

var i interface {} 
// Holds any type

Type Assertion

v, ok = i.(int)
// Type Assertion

Type switch

switch v := i.(type) {
    case ...


fmt.Print looks for Stringer interface to print values

func (v Type) String() string {
    return fmt.Sprintf("asdasd")


error interface implements Error() method

func (e *MyErr) Error() string {
    return "Error message"


ch <- v // Send v to channel
v := <- ch // Recv val from channel

ch := make(chan int)
// Create the chennel

ch := make(chan int, 100)
// buffered channel, blocks

for i := range c {
    //recv value repeatedly until closed

close(ch) // close channel

v, ok := <- ch
// Ok is false if channel closed


select {
    case c <- x:
// Block until some case can run
// Randomly select one if multiple are ready


type Counter struct {
    v int
    mux sync.Mutex

c:= Counter{v:1}
func (c *Counter) {
    defer c.mux.Unlock()
    c.v = 3