Basic Syntax

Package Structure

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

Variables & Types

// declare a variable
var a int
var b int = 10 // with initialization
var c = 10     // type inferred
d := 10        // short hand

// declare multiple variables
var (
    e int
    f int = 10
    g = 10
    h := 10
)

// declare constants
const i int = 10
const j = 10
const (
    k int = 10
    l = 10
)

// array (fixed size)
var m [5]int
n := [5]int{1, 2, 3, 4, 5}
o := [...]int{1, 2, 3, 4, 5}  // size inferred

// slice (dynamic size)
p := []int{1, 2, 3, 4, 5}
q := n[1:3] // slice of n from index 1 to 3
r := n[:3]  // slice of n from index 0 to 3
s := make([]int, 5) // make a slice with length 5

// map (key-value)
t := map[string]int{
    "a": 1,
    "b": 2,
}
for k, v := range t {
    fmt.Println(k, v)
}

// types
int, int8, int16, int32, int64
uint, uint8, uint16, uint32, uint64, uintptr
float32, float64
complex64, complex128
byte // uint8
rune // (int32) a Unicode code point
string

// type conversion
i := 42
f := float64(i)
u := uint(f)

Operators

// arithmetic
+ - * / % ++ --
// `/` is integer division if both operands are integers.
// `%` is the remainder of the division.

// relational
== != > < >= <=

// logical
&& || !
// `&&` and `||` are short-circuit operators.

// bitwise
& | ^ << >>
&^ // bit clear (AND NOT)

// assignment
= += -= *= /= %= <<= >>= &= |= ^=

Control Flow

Branching

// if-else
if a > 10 {
    fmt.Println("a is greater than 10")
} else if a < 10 {
    fmt.Println("a is less than 10")
} else {
    fmt.Println("a is equal to 10")
}

// switch
switch a {
case 1:
    fmt.Println("a is 1")
case 2:
    fmt.Println("a is 2")
default:
    fmt.Println("a is neither 1 nor 2")
}
switch {  // switch true for long-chain if-else
case t.Hour() < 12:
    fmt.Println("Good morning!")
case t.Hour() < 17:
    fmt.Println("Good afternoon.")
default:
    fmt.Println("Good evening.")
}

Looping

// for (while-style)
x := 0
for x < 5 {
    fmt.Println(x)
    x++
}

// for (C-style)
for i := 0; i < 5; i++ {
    fmt.Println(i)
}

// for (range)
y := []int{1, 2, 3, 4, 5}
for i, v := range y {
    fmt.Println(i, v)
}
for _, v := range y {  // ignore index
    fmt.Println(v)
}

// infinite loop
for {
    fmt.Println("infinite loop")
}

break    // exit loop
continue // skip to next iteration

Defer

A defer statement defers the execution of a function until the surrounding function returns.

// defer
func main() {
    defer fmt.Println("world")
    fmt.Println("hello")
} // output: hello world

// defer stack
func main() {
    for i := 0; i < 5; i++ {
        defer fmt.Println(i)
    }
} // output: 4 3 2 1 0

Functions

// declaration
func add(a int, b int) int {
    return a + b
}
// call 
add(1, 2)

// multiple return values
func swap(a int, b int) (int, int) {
    return b, a
}
func map(str []string, f func(string) string) []string {
    result := make([]string, len(str))
    for i, v := range str {
        result[i] = f(v)
    }
    return result
} 

// named return values
func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return
}

Pointers

// declaration
var a int = 10
var b *int = &a // b is a pointer to a
var nothing *int // `nil` pointer

// dereference
fmt.Println(*b) // 10

// pass by reference (pointer's value)
func change(a *int) {
    *a = 20
}
change(&a)     // `&` address of
fmt.Println(a) // 20

Structs

// declaration
type person struct {
    name string
    age  int
}

// initialization
p := person{"Alice", 20}
q := person{
    name: "Bob",
    age:  30,
}
fmt.Println(p.name) // access fields

// receiver function (method)
func (p person) greet() {
    fmt.Println("Hello, my name is", p.name)
}
p.greet() // call method

// pointer receiver function
func (p *person) grow() {
    p.age++  // auto dereferenced
}
p.grow()

Interfaces

// declaration
type drawable interface {
    info() string
    draw()
}

// implementation
type circle struct {
    radius int
}
func (c circle) info() string {
    return "Circle"
}
func (c circle) draw() {
    fmt.Println("Drawing circle")
}

// polymorphism
func drawShape(d drawable) {
    fmt.Println(d.info())
    d.draw()
}
drawShape(circle{10})

// empty interface
func describe(i interface{}) {  // any type
    fmt.Printf("(%v, %T)\n", i, i)
}
describe(42)
describe("hello")