This is a post of Golang Summary, for me to check out long time not writing program using Golang since 3 years ago.

1. Package
1.1. Basic
- Every Go program is made up of packages. Which means, the
basic organization unitof Go program ispackage, not file. - Programs start running in package
main. - By convention, the package name is the same as the last element(
sub-package) of the import path.
For instance, themath/randpackage comprises files that begin with the statementpackage rand. Import packages using
factored import statement. (variables declaration can also usefactored declare)1
2
3
4
5
6
7
8
9
10package main
import (
"fmt"
"math"
)
func main() {
fmt.Printf("Now you have %g problems.", math.Sqrt(7))
}Not using next statements, since they should be highly in a cohesion:
1
2import "fmt"
import "math"By convention, a name starts with capital letter will be exported. Eg.
Piinmathpackage.
This means, when usingother package functions/fields, it always starts withCapitalLetters.
2. Functions
2.1. Basic
Two or more
consecutivenamed function parameters share a same type, we can omit the type all but the last.x int, y intcan be shorted tox, y int. Eg.1
2
3func swap(x, y string) (string, string) {
return y, x
}Naked return statementsshould be used in only short function.1
2
3
4
5func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
3. Variables
3.1. Basic
varstatement can be a package/function level1
2
3
4
5
6
7
8
9
10package main
import "fmt"
var i, j int = 1, 2
func main() {
var c, python, java = true, false, "no!" // variables type can be taken from the initializer
fmt.Println(i, j, c, python, java)
}Short Variablecan only be used inside a function using:=1
2
3
4
5
6
7
8
9
10
11package main
import "fmt"
func main() {
var i, j int = 1, 2
k := 3 // short variable
c, python, java := true, false, "no!" // short variable
fmt.Println(i, j, k, c, python, java)
}
4. Types
4.1. Basic
Go Basic Types
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32
// represents a Unicode code point
float32 float64
complex64 complex128The
int,uint, anduintptrtypes are usually 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems. Normally we useintunless you have a specific reason to use a sized or unsigned integer type.Zero Values: Variables will be assigned to Zero Values if no explicit initial value. Eg.0for numeric types,falsefor boolean type,""for strings.T(v)will convert value V to type T.Explicit Type Conversion is required in Go.
5. Constants
5.1. Basic
constis used beforeConstantsdeclaration. Type can be onlycharacter/string/boolean/numeric values.
6. Flow control statements: For/If/Switch/Defer
6.1. Basic
For loop, the
initandpoststatements are optional:1
2
3
4sum := 0
for i := 0; i < 10; i++ {
sum += i
}or
1
2
3
4sum := 1
for ; sum < 1000; {
sum += sum
}foris Go’swhilewithout;1
2
3
4sum := 1
for sum < 1000 { // just is while
sum += sum
}infinite loop
1
2
3
4
5
6package main
func main() {
for {
}
}for loop with range, can iterate the slices.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package main
import "fmt"
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
func main() {
for i, v := range pow { // return with 2 values: index and corresponding value copy of data in that index
fmt.Printf("2**%d = %d\n", i, v)
}
for i := range pow { // return only the index
pow[i] = 1 << uint(i) // == 2**i
}
for _, value := range pow { // ignore the index, only need the value
fmt.Printf("%d\n", value)
}
}one short statementbefore if condition, to make statements more closed for readable1
2
3
4
5
6
7
8
9func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim { // only one statement is allowed here
return v
} else {
fmt.Printf("%g >= %g\n", v, lim)
}
// After this if-else statement, v is not accessible any more
return lim
}switchwithout break statement, but still can provide with ashort statementbefore.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os { // allow one short statement
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.") // no need for break
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.", os)
}
}switch without conditionis the same asswitch true.1
2
3
4
5
6
7
8
9t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}deferfunction will be called once its enclosed function is returned. However,deferred function parameterswill be eval inside the enclosed functionsimmediatelyat its declaration position. A stack will be generated when there isdeferstatements in go program and will be popped out using LIFO sequence.
7. Data Structure: Pointer/Struct/Slice/Map
7.1. Basic
Pointer:*Tis pointer used to point totype T.& operatorwill create a pointer that point to existing value.* operatorwill eval the value of the target variable.Struct: collections of fields. We can use(*p).Xto accessstruct pointer p‘s fieldX. To make it simple, we can just usep.X, which is implicit dereference.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19package main
import "fmt"
type Vertex struct {
X, Y int
}
var ( // use factored declare statements
v1 = Vertex{1, 2} // has type Vertex
v2 = Vertex{X: 1} // Y:0 is implicit
v3 = Vertex{} // X:0 and Y:0
)
func main() {
p := &Vertex{1, 2} // has type *Vertex
fmt.Println(v1, p, v2, v3)
}[n]Tmeansan array of type T with n size. E.g,var a [10]int, meansint a[10]in Java.[]Tmeansa slice of type T with dynamic size. E.g,a[1:4]create a slice, containing elements in a indexed from 1 to 3.Slices does not store data, just describe a segment of the underlying array. They are more of array's references.- Array syntax and slice syntax:
[3]bool{true, true, false}is an array,[]bool{true, true, false}is an anonymous array but then referenced using a slice. Slice default upper/lower boundary is array length/0.E.g1
2
3
4
5
6var a [10]int
// next 4 slices are equal
a[0:10]
a[:10]
a[0:]
a[:]nil slice: capacity/length is 0, and there is not underlying array for this slice. E.g,var s []int.Use
make([]slice, len, cap)to create a slice. E.g,1
2a := make([]int, 5) // len(a)=5
b := make([]int, 0, 5) // len(b)=0, cap(b)=5Append elements to a slice and return with a
new generated slice:func append(s []T, vs ...T) []T.1
2
3
4
5
6
7
8
9
10
11
12
13func main() {
var s []int
printSlice(s)
s = append(s, 0) // append works on nil slices.
printSlice(s)
s = append(s, 1) // The slice grows as needed.
printSlice(s)
s = append(s, 2, 3, 4) // We can add more than one element at a time.
printSlice(s)
}map[KeyType]ValueTypeis used to map keys to values.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main
import "fmt"
type Vertex struct {
Lat, Long float64
}
var m map[string]Vertex
func main() {
m = make(map[string]Vertex)
m["Bell Labs"] = Vertex{
40.68433, -74.39967,
}
var m = map[string]Vertex{ // styntax is like struct
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": Vertex{
37.42202, -122.08408,
},
}
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967}, // Omit the Vertex type
"Google": {37.42202, -122.08408},
}
fmt.Println(m["Bell Labs"])
}map basic operations:- insert/update key:
m[key] = elem - get key:
elem = m[key] - delete key:
delete(m, key) - double assignment to check key existence:
elem, ok := m[key]// ok will be true if exist, otherwise false
- insert/update key:
Function Closure: A closure is a function value that references variables from outside its body.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21package main
import "fmt"
func adder() func(int) int { // returns a closure
sum := 0
return func(x int) int { // Each closure is bound to its own sum variable
sum += x
return sum
}
}
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
8. Method and Interface
8.1. Basic
Method: A function with a specialreceiverargument.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 { // receiver's type declaration and method declaration must be in the same package
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(v.Abs()) // likes v receives the message of Abs, v is the receiver
}Interface: Aninterface typeis defined as a set of method signatures. As long as some data structure implements all the methods declared int thisinterface type, it is an instance of thisinterface type.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42package main
import (
"fmt"
"math"
)
type Abser interface {
Abs() float64
}
func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f // a MyFloat implements Abser, since MyFloat does have a method `Abs()`
a = &v // a *Vertex implements Abser, since *Vertex does have a method `Abs()`
// In the following line, v is a Vertex (not *Vertex)
// and does NOT implement Abser.
a = v
fmt.Println(a.Abs())
}
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}Interfaces are implemented implicitly. Implicit interfaces decouple the definition of an interface from its implementation, which could then appear in any package without prearrangement. In a word, you can define the interface/standard, everyone can implement it. This is a
pluggable thought into a language.Empty Interface: interfaces which has 0 methods. They can be used toprocess un-predictabletypes. Eg. fmt.Println() acceptinterface{}parameters.Type assertion: provides access to an interface value’s underlying concrete value. usingt := i.(T)to assertinterface idoes have a concretetype Tand assign this type’s underly value tovariable t.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package main
import "fmt"
func main() {
var i interface{} = "hello"
s := i.(string)
fmt.Println(s)
s, ok := i.(string)
fmt.Println(s, ok)
f, ok := i.(float64) // no panic here, since we still have ok as false and f will be nil value of float64/type T
fmt.Println(f, ok)
f = i.(float64) // panic: interface conversion: interface {} is string, not float64
fmt.Println(f)
}Type switches: permits several type assertions in series.1
2
3
4
5
6
7
8switch v := i.(type) {
case T:
// here assert v has type T
case S:
// here assert v has type S
default:
// no match; here v has the same type as i
}For Example,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package main
import "fmt"
func do(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("Twice %v is %v\n", v, v*2)
case string:
fmt.Printf("%q is %v bytes long\n", v, len(v))
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}
func main() {
do(21) // output: Twice 21 is 42
do("hello") // output: "hello" is 5 bytes long
do(true) // output: I don't know about type bool!
}Stringerinterface infmtpackage. It has a methodString() string.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package main
import "fmt"
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
fmt.Println(a, z) // output: Arthur Dent (42 years) Zaphod Beeblebrox (9001 years)
}errorinterface infmtpackage. it has a methodError() string. This is the so-called customized exception.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package main
import (
"fmt"
"math"
)
type ErrNegativeSqrt float64 // we can wrap the value itself into this error and print info related to this obj inside the Error() method, this is a good practice
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}
func Sqrt(x float64) (float64, error) {
if x < 0 {
return 0, ErrNegativeSqrt(x)
}
return math.Sqrt(float64(x)), nil
}
func main() {
fmt.Println(Sqrt(2)) // output: 1.4142135623730951 <nil>
fmt.Println(Sqrt(-3)) // output: 0 cannot Sqrt negative number: -3
}
9. Concurrency
9.1. Basic
Goroutine: lightweight thread managed by the Go runtime.go f(x, y, z)starts a new goroutine running function f(x,y,z). The evaluation of parameters happens immediately, but execution happens later.Chanel: typed conduit through which you can send and receive values with the channel operator,<-.
The data flows in the direction of the arrow.1
2
3ch <- v // Send v to channel ch.
v := <-ch // Receive from ch, and
// assign value to v.Create a channel before using it:
ch := make(chan int).Range/Close a channel, useclose(ch)from producer side to close a channel to tell the receiver no more items produced.v, ok := <-ch, theokwill be false ifch is already closed.for i := range cwill get value from channelcontinuouslyuntil it’s closed1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package main
import (
"fmt"
)
func fibonacci(n int, c chan float64) {
x, y := 0.0, 1.0
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan float64, 100)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}select statement: select statement lets a goroutine wait on multiple communication operations. Itblocksuntil one of its cases can run, then it executes that case. It choosesone at random if multiple are ready.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for { // this is a inf loop
select { // implicitly we do have a goroutine here, it waits on multiple channels
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return // this is quit condition for this inf loop
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() { // 1st goroutine
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0 // removing this line will get this program stuck
}()
fibonacci(c, quit) // 2nd goroutine, block next all statements
quit <- 1 // this statement will never be executed since the above statement blocks if `quit <- 0` is removed
}Time elapsed channel:time.Tickandtime.After.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23package main
import (
"fmt"
"time"
)
func main() {
tick := time.Tick(1000 * time.Millisecond) // every 1 second, get a value from this channel
boom := time.After(5000 * time.Millisecond) // after 5 second, get a value from this channel
for {
select {
case x := <-tick:
fmt.Println("tick.", x)
case x := <-boom:
fmt.Println("BOOM!", x)
return
default:
// fmt.Println(" .")
time.Sleep(500 * time.Millisecond)
}
}
}Output:
1
2
3
4
5tick. 2009-11-10 23:00:01 +0000 UTC m=+1.000000001
tick. 2009-11-10 23:00:02 +0000 UTC m=+2.000000001
tick. 2009-11-10 23:00:03 +0000 UTC m=+3.000000001
tick. 2009-11-10 23:00:04 +0000 UTC m=+4.000000001
BOOM! 2009-11-10 23:00:05 +0000 UTC m=+5.000000001range/closeorselect/quit channelorTick/Afterto do the goroutine synchronization using channel.sync.MutexhasLock/Unlockwhich can also provideshared-memory mechanism synchronization.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39package main
import (
"fmt"
"sync"
"time"
)
// SafeCounter is safe to use concurrently.
type SafeCounter struct {
v map[string]int
mux sync.Mutex
}
// Inc increments the counter for the given key.
func (c *SafeCounter) Inc(key string) {
c.mux.Lock()
// Lock so only one goroutine at a time can access the map c.v.
c.v[key]++
c.mux.Unlock()
}
// Value returns the current value of the counter for the given key.
func (c *SafeCounter) Value(key string) int {
c.mux.Lock()
// Lock so only one goroutine at a time can access the map c.v.
defer c.mux.Unlock()
return c.v[key]
}
func main() {
c := SafeCounter{v: make(map[string]int)}
for i := 0; i < 1000; i++ {
go c.Inc("somekey")
}
time.Sleep(time.Second)
fmt.Println(c.Value("somekey"))
}
10. Build
10.1. use golang docker image to build
Use golang:1.9 to build go program
https://github.com/docker-library/docs/tree/master/golang#compile-your-app-inside-the-docker-container1
2
3
4
5
6
7APPNAME=passport
BUILD_PATH=/usr/src/${APPNAME}
GOLANG_IMG="golang:1.9"
docker run --rm -v "$PWD":"${BUILD_PATH}" -w ${BUILD_PATH} -e GOPATH="${BUILD_PATH}" -e GOBIN="${BUILD_PATH}/bin" -e CGO_ENABLED=0 -e GOOS=linux -e GOARCH=amd64 golang:1.9 go get -v && go build -a -installsuffix cgo \
-ldflags "-s -w" \
-o "${BUILD_PATH}/${APPNAME}" .
References: