Go - Cheat Sheet
Debug
import "log"
log.Println("debug");
Why should I use log.Println instead of fmt.Println?
Two things are different:
- Printing via package log is safe from concurrent goroutines (while plain fmt isn't)
- Log can add timing information automatically.
So these are two completely different things. log is for logging and fmt for formatting. (Okay, log uses the same verbs and flags, but that is just convenient).
Reference: https://stackoverflow.com/questions/19646889/why-should-i-use-log-println-instead-of-fmt-println
Basic Types & Variables
// Variable declaration
var msg string
var msg = "Hello, world!"
var msg string = "Hello, world!"
var x, y int
var x, y int = 1, 2
var x, msg = 1, "Hello, world!"
msg = "Hello"
// Declaration list
var (
x int
y = 20
z int = 30
d, e = 40, "Hello"
f, g string
)
// Shortcut of above (Infers type)
msg := "Hello"
x, msg := 1, "Hello"
Strings
str := "Hello"
str2 := `Multiline
string`
str3 := "Multiline \nstring"
str3 := "Multiline" + " " + "string"
// Strings are of type string.
Numbers
// Typical types
num := 3 // int
num := 3. // float64
num := 3 + 4i // complex128
num := byte('a') // byte (alias for uint8)
// Other types
var u uint = 7 // uint (unsigned)
var p float32 = 22.7 // 32-bit float
Arrays
// var numbers [5]int
numbers := [...]int{0, 0, 0, 0, 0}
// Arrays have a fixed size.
Slices
slice := []int{2, 3, 4}
slice2 := []byte("Hello")
// Slices have a dynamic size, unlike arrays.
Pointers
func main () {
b := *getPointer()
fmt.Println("Value is", b)
}
func getPointer () (myPointer *int) {
a := 234
return &a
}
a := new(int)
*a = 234
- Pointers point to a memory location of a variable. Go is fully garbage-collected.
- See: Pointers
Type Conversions
i := 2
f := float64(i)
u := uint(i)
- See: Type conversions
Hello world
// File: hello.go
package main
import "fmt"
func main() {
message := greetMe("world")
fmt.Println(message)
}
func greetMe(name string) string {
return "Hello, " + name + "!"
}
$ go run hello.go
Constants
const Phi = 1.618
const Size int64 = 1024
const x, y = 1, 2
const (
Pi = 3.14
E = 2.718
)
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
// It then uses the `iota` identifier to tell the Go compiler you want the first value to start at 0, and then increment by 1 for each following constant
Go casting
package main
import (
"fmt"
)
func main() {
var num1 int32 = 10
var num2 int64
num2 = int64(num1)
fmt.Println("num1: ", num1)
fmt.Println("num2: ", num2)
}
Flow Control
Conditional
if day == "sunday" || day == "saturday" {
rest()
} else if day == "monday" && isTired() {
groan()
} else {
work()
}
- See: If
Statements in if
if _, err := doThing(); err != nil {
fmt.Println("Uh oh")
}
- A condition in an if statement can be preceded with a statement before a ;. Variables declared by the statement are only in scope until the end of the if.
- See: If with a short statement
Switch
switch day {
case "sunday":
// cases don't "fall through" by default!
fallthrough
case "saturday":
rest()
default:
work()
}
- See: Switch
For loop
for count := 0; count <= 10; count++ {
fmt.Println("My counter is at", count)
}
- See: For loops
For-Range loop
entry := []string{"Jack","John","Jones"}
for i, val := range entry {
fmt.Printf("At position %d, the character %s is present\n", i, val)
}
- See: For-Range loops
While loop
n := 0
x := 42
for n != x {
n := guess()
}
See: Go’s “while”
Functions
Lambdas
myfunc := func() bool {
return x > 10000
}
- Functions are first class objects.
Multiple return types
a, b := getMessage()
func getMessage() (a string, b string) {
return "Hello", "World"
}
Named return values
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
- By defining the return value names in the signature, a return (no args) will return variables with those names.
- See: Named return values
Packages
Importing
import "fmt"
import "math/rand"
// OR
import (
"fmt" // gives fmt.Println
"math/rand" // gives rand.Intn
)
// Both are the same.
- See: Importing
Aliases
import r "math/rand"
...
r.Intn()
Packages
package hello
- Every package file has to start with package.
Exporting names
func Hello () {
···
}
- Exported names begin with capital letters.
- See: Exported names
Concurrency
Goroutines
func main() {
// A "channel"
ch := make(chan string)
// Start concurrent routines
go push("Moe", ch)
go push("Larry", ch)
go push("Curly", ch)
// Read 3 results
// (Since our goroutines are concurrent,
// the order isn't guaranteed!)
fmt.Println(<-ch, <-ch, <-ch)
}
func push(name string, ch chan string) {
msg := "Hey, " + name
ch <- msg
}
- Channels are concurrency-safe communication objects, used in goroutines.
- See: Goroutines, Channels
Buffered Channels
ch := make(chan int, 2)
ch <- 1
ch <- 2
ch <- 3
// fatal error:
// all goroutines are asleep - deadlock!
- Buffered channels limit the amount of messages it can keep.
- See: Buffered channels
Closing Channels
// Closes a channel
ch <- 1
ch <- 2
ch <- 3
close(ch)
// Iterates across a channel until its closed
for i := range ch {
···
}
// Closed if ok == false
v, ok := <- ch
- See: Range and close
WaitGroup
import "sync"
func main() {
var wg sync.WaitGroup
for _, item := range itemList {
// Increment WaitGroup Counter
wg.Add(1)
go doOperation(&wg, item)
}
// Wait for goroutines to finish
wg.Wait()
}
func doOperation(wg *sync.WaitGroup, item string) {
defer wg.Done()
// do operation on item
// ...
}
- A WaitGroup waits for a collection of goroutines to finish. The main goroutine calls Add to set the number of goroutines to wait for. The goroutine calls wg.Done() when it finishes.
- See: WaitGroup
Error Control
Defer
func main() {
defer fmt.Println("Done")
fmt.Println("Working...")
}
- Defers running a function until the surrounding function returns. The arguments are evaluated immediately, but the function call is not ran until later.
- See: Defer, panic and recover
Deferring functions
func main() {
defer func() {
fmt.Println("Done")
}()
fmt.Println("Working...")
}
// Lambdas are better suited for defer blocks.
func main() {
var d = int64(0)
defer func(d *int64) {
fmt.Printf("& %v Unix Sec\n", *d)
}(&d)
fmt.Print("Done ")
d = time.Now().Unix()
}
// The defer func uses current value of d, unless we use a pointer to get final value at end of main.
Structs
Defining
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
v.X = 4
fmt.Println(v.X, v.Y)
}
- See: Structs
Literals
v := Vertex{X: 1, Y: 2}
// Field names can be omitted (省略)
v := Vertex{1, 2}
// Y is implicit (隐含的)
v := Vertex{X: 1}
- You can also put field names.
Pointers to structs
v := &Vertex{1, 2}
v.X = 2
- Doing v.X is the same as doing (*v).X, when v is a pointer.
Methods
Receivers
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X * v.X + v.Y * v.Y)
}
- There are no classes, but you can define functions with receivers.
- See: Methods
Mutation
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
v := Vertex{6, 12}
v.Scale(0.5)
// `v` is updated
- By defining your receiver as a pointer (*Vertex), you can do mutations.
- See: Pointer receivers
Interfaces
A basic interface
type Shape interface {
Area() float64
Perimeter() float64
}
Struct
type Rectangle struct {
Length, Width float64
}
- Struct
Rectangle
implicitly implements interface Shape by implementing all of its methods.
Methods
func (r Rectangle) Area() float64 {
return r.Length * r.Width
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Length + r.Width)
}
- The methods defined in
Shape
are implemented inRectangle
.
Interface example
func main() {
var r Shape = Rectangle{Length: 3, Width: 4}
fmt.Printf("Type of r: %T, Area: %v, Perimeter: %v.", r, r.Area(), r.Perimeter())
}