Skip to main content

Go Basic Notes

CLI

Installation

sudo apt install golang
echo "export GOPATH=$HOME/gopath"
echo "export PATH=$PATH:$GOPATH/bin"
go env

Basic Command

go version
go run main.go
go fmt /path/to/test
  • go 的大部分工具的作用基本单位为 package(directories)

Build

# generate library
go build path/to/libpack
go install path/to/libpack

# generate binary
go install path/to/mainpack

Test

# path/to/pack/demo.go
# path/to/pack/demo_test.go
go test path/to/pack

Clean

go clean -i path/to/pack

Modules

  • Remote packages.
  • $GOPATH/bin/hello.
go get github.com/golang/example/hello

Packages

package and import

  • for path/to/pack:
package pack
import (
"path/to/pack"
)
  • 只有首字母大写的函数才可被成功导出, 首字母小写的函数为文件私有函数

Variable

Type Declaration

  • Go 将类型置于变量名后的理由: reads clearly, from left to right
  • := 不可用在函数外
// 简写类型/赋值
var i,j int = 1, 2

// 省略类型
var c, python, java = true, false, "no!"

// 省略 var 关键字
javascript, ruby, cpp:= true, false, "no!"

// 声明块
import (
"math/cmplx"
)

var (
ToBe bool = false
MaxInt uint64 = 1<<64 - 1
z complex128 = cmplx.Sqrt(-5 + 12i)
)

Type conversions

var x int = 3
var y uint = uint(x)
z := uint(x)

struct

type Vertex struct {
X int
Y int
}

var (
v1 = Vertex{1, 2}
v2 = Vertex{X: 1} // Y: 0
v3 = Vertex{} // X: 0, Y: 0
vp = &Vertex{1, 2} // *Vertex
)

array

数组的长度是其类型的一部分

var a [2]string
a[0] = "Hello"
a[1] = "Golang"

fmt.Println(a[0], a[1])
fmt.Println(a)

slice

  • s[lo:lo] == nil
p := []int{2, 3, 5, 7, 11, 13}

fmt.Println("p[1:4] ==", p[1:4])
fmt.Println("p[:3] ==", p[:3]) // p[0:3] => 0, 1, 2
fmt.Println("p[4:]" ==, p[4:]) // p[4:len(p)-1] => 4, ..., len(p)-2
  • make 函数创建 slice
a := make([]int, 5)     // len(a) = 5
b := make([]int, 0, 5) // len(b) = 0, cap(b) = 5
b = b[:cap(5)] // len(b) = 5, cap(b) = 5
  • len && cap
// just shorten/extend, not drop elements
// change len(p), keep cap(p)
p = p [:0]
p = p[:4]

// drop its elements
// change len(p) and cap(p)
p = p[2:]
  • append
append(s, 2, 3, 4)
  • range(iterator): 返回 2 个值(index int, element copy(s[index]) T), 在每一次迭代 index+=1
pow := []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {
for i := range pow {
fmt.Printf("index == %d\n", i)
}

for _, v := range pow {
fmt.Printf("value == %d\n", v)
}

for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
}

map

type Vertex struct {
Lat, Long float64
}

var m map[string]Vertex = make(map[string]Vertex)
m["Bell Labs"] = Vertex{
40.68433, -74.39967
}

ml := map[string]Vertex{
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": {37.42202, -122.08408},
}

delete(m, "Bell Labs")

element, ok_flag := m["Google"]

Flow Control

if

if x < 0 {
return true
}

// scope of v: only in if/else statement
if v := math.Pow(x, n); v < lim {
return v
} else {
fmt.Printf("%g >= %g\n", v, lim)
}

for

for sum < 1000 {
sum += sum
}

// scope of i: only in for statement
for i := 0; i < 10; i++ {
sum += i
}

switch

  • switch 中的 case 自动 break(除非使用 fallthrough 语句)
switch time.Saturday {
case today+0:
fmt.Println("Today.")
case today+1:
fmt.Println("Tomorrow.")
case today+2:
fmt.Println("In two days.")
default:
fmt.Println("Too far away.")
}

// scope of os: only in switch statement
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
fmt.Printf("%s", os)
}

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

defer

defer 语句会将函数执行延迟至上层函数返回处(函数参数会立刻生成):

执行时机

函数设置返回值后, 即将返回调用函数前(若 defer 函数修改返回变量, 则会造成返回值与预期不一致)

func main() {
defer fmt.Println("!")
defer fmt.Println("world")
fmt.Println("hello")
}

=>

func main() {
fmt.Println("hello")
fmt.Println("world")
fmt.Println("!")
}

实质

return_value = xxx -> invoke defer functions(stack) -> return void

func f() (result int) {
defer func() {
result++
}()

return 0
}

=>

func f() (result int) {
result = 0

func() {
result++
}()

return
}

应用场景

  • 资源回收
mu.Lock()
defer mu.Unlock()
  • panic 异常的捕获
func f() {
defer func() {
if r:= recover(); r!= nil {
fmt.Println("Recovered in f", r)
}
}()

fmt.Println("Calling g.")
g()
fmt.Println("Returned normally from g.")
}

func g() {
panic("ERROR")
}
  • 保证语句(在发生异常的情况下)始终会被执行
  • 有意修改返回值

Function

Parameters and Return Value

  • 简写参数类型
  • 多值返回函数
  • 命名返回值(注释文档)
func swap(x, y string) (string, string) {
return y, x
}

func swap_(x, y string) (x_, y_ string) {
x_, y_ = y, x
return
}

func main() {
a, b := swap("hello", "golang")
a_, b_ := swap_("hello", "golang")
fmt.Println(a, b)
fmt.Println(a_, b_)
}

Methods

  • Go 中没有 class, 但可以在 struct/同一个包内的 type 上(receiver)定义方法
type Vertex struct {
X, Y float64
}

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

func main() {
v := &Vertex{3, 4}
fmt.Println(v.Abs())
}
type MyFloat float64

func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
} else {
return float64(f)
}
}

func main() {
f := MyFloat(-math.Sqrt2)
fmt.Println(f.Abs())

Receiver

  • pointer receiver: 可以改变原值(call by reference)
  • value receive: 不可以改变原值(call by value)
  • 调用 methods 时, 可以不考虑 v 是 value/pointer, go 会自动处理
func (v *Vertex) changeV() {
v.X += 1
v.Y += 1
}

v.changeV() => (&v).changeV()
func (v Vertex) Abs() {
return abs(v)
}

(&v).Abs() => v.Abs()
  • Best Practice: 在同一个类型上定义的所有方法最好统一 receiver 类型(全部 value receivers 或 全部 pointer receivers)

Interface

(value, type)

var i I
var t *T
i = t // => (nil, *T)
var i I     // => (nil, nil)

Type Assertions

  • 单返回值: 断言失败时产生 panic
  • 双返回值: 断言失败时不产生 panic
// create empty interface, ("hello", string)
var i interface{} = "hello"

s := i.(string)
s, ok := i.(string) // => true
f, ok := i.(float64)// => false(no panic)
f := i.(float64) // => false with panic
  • type switches
switch v := i.(type) {
case int;
fmt.Println("Int.")
case string:
fmt.Println("String.")
default:
fmt.Printf("Other type.")
}

Concurrent

goroutine

go f(x, y, z)   // => execute in a new goroutine with share memory

channels

  • typed conduit(类型管道)
  • block execution
var c chan int = make(chan int)

c <- sum // send sum to channel c
v := <-c // receive from channel c, assign value to v
func sum(s []int, c chan int) {
sum := 0

for _, v := range s {
sum += v
}

c <- sum
}

func main() {
s := []int{7, 2, 8, -9, 4, 0}

c := make(chan int)

go sum(s[:len(s)/2], c)
go sum(s[len(s/2):], c)

x, y = <-c, <-c

fmt.Println(x, y, x+y)
}

select

  • select(当所有情况都不满足时)可被阻塞
for {
select {
case c <- x:
x, y = y, x+y
case <- quit:
fmt.Println("quit")
return
}
}

Worker Pools

package main

import "fmt"
import "time"

func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Println("worker", id, "processing job", j)
time.Sleep(time.Second)
results <- j * 2
}
}

func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)

for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}

for j := 1; j <= 9; j++ {
jobs <- j
}

close(jobs)

for a := 1; a <= 9; a++ {
<-results
}
}