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 unit
of 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/rand
package 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.
Pi
inmath
package.
This means, when usingother package functions/fields
, it always starts withCapitalLetters
.
2. Functions
2.1. Basic
Two or more
consecutive
named function parameters share a same type, we can omit the type all but the last.x int, y int
can be shorted tox, y int
. Eg.1
2
3func swap(x, y string) (string, string) {
return y, x
}Naked return statements
should 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
var
statement 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 Variable
can 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
, anduintptr
types are usually 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems. Normally we useint
unless 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.0
for numeric types,false
for 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
const
is used beforeConstants
declaration. Type can be onlycharacter/string/boolean/numeric values
.
6. Flow control statements: For/If/Switch/Defer
6.1. Basic
For loop, the
init
andpost
statements 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
}for
is Go’swhile
without;
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 statement
before 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
}switch
without break statement, but still can provide with ashort statement
before.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 condition
is 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.")
}defer
function will be called once its enclosed function is returned. However,deferred function parameters
will be eval inside the enclosed functionsimmediately
at its declaration position. A stack will be generated when there isdefer
statements in go program and will be popped out using LIFO sequence.
7. Data Structure: Pointer/Struct/Slice/Map
7.1. Basic
Pointer
:*T
is pointer used to point totype T
.& operator
will create a pointer that point to existing value.* operator
will eval the value of the target variable.Struct
: collections of fields. We can use(*p).X
to 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]T
meansan array of type T with n size
. E.g,var a [10]int
, meansint a[10]
in Java.[]T
meansa 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]ValueType
is 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 specialreceiver
argument.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 type
is 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-predictable
types. Eg. fmt.Println() acceptinterface{}
parameters.Type assertion
: provides access to an interface value’s underlying concrete value. usingt := i.(T)
to assertinterface i
does have a concretetype T
and 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!
}Stringer
interface infmt
package. 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)
}error
interface infmt
package. 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
, theok
will be false ifch is already closed
.for i := range c
will get value from channelcontinuously
until 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. Itblocks
until 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.Tick
andtime.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/close
orselect/quit channel
orTick/After
to do the goroutine synchronization using channel.sync.Mutex
hasLock/Unlock
which 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: