Notes, Effective Go

Contents
  1. 1. Acknowledge
  2. 2. Syntax
    1. 2.1. Basics
    2. 2.2. Flow control
    3. 2.3. More types
  3. 3. Methods and interfaces
    1. 3.1. methods
    2. 3.2. interfaces
  4. 4. Concurrency
    1. 4.1. goroutines
    2. 4.2. channels
    3. 4.3. buffered Channels
    4. 4.4. range and close
    5. 4.5. select
    6. 4.6. sync.Mutex
  5. 5. Language specification
    1. 5.1. Formatting
    2. 5.2. Commentary
    3. 5.3. Names
    4. 5.4. Semicolons
    5. 5.5. Redeclaration and reassignment
    6. 5.6. Data
    7. 5.7. Arrays
    8. 5.8. The blank identifier
    9. 5.9. Concurrency
    10. 5.10. Errors
    11. 5.11. A web server
  6. 6. Reference

Last Modified: 2017-08-23 11:46:10

Hard to say, I finished Go, for about 20 days (except busy for 1 week and training for 1 week).

Such a long time, 1 week to learn a new language, having known a little about concurrency.

Not so bad.

Acknowledge

Go is a new language. Although it borrows ideas from existing languages, it has unusual properties that make effective Go programs different in character from programs written in its relatives.
The notes below was written from the view of a CPP programmer.

  • and a hello world
    1
    2
    3
    4
    5
    6
    7
    package main
    import "fmt"
    func main() {
    fmt.Println("Hello, World")
    }

Syntax

Basics

  • function

    1
    2
    3
    4
    5
    6
    7
    8
    func swap(x, y string) (string, string) {
    return y, x
    }
    func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return
    }
  • variable

    1
    2
    3
    4
    5
    var c, python, java bool
    var i, j int = 1, 2
    var c, python, java = true, false, "no!"
    k := 3 // only can be used inside function
  • basic types

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    bool
    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 complex128
    // The int, uint, and uintptr types are usually 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems.
  • var blocks

    1
    2
    3
    4
    5
    var (
    ToBe bool = false
    MaxInt uint64 = 1<<64 - 1
    z complex128 = cmplx.Sqrt(-5 + 12i)
    )
  • zero values
    Variables declared without an explicit initial value are given their zero value.
    0 for numeric types,
    false for the boolean type, and
    "" (the empty string) for strings.

  • type conversions

    1
    2
    3
    4
    5
    6
    7
    var i int = 42
    var f float64 = float64(i)
    var u uint = uint(f)
    // more simply
    i := 42
    f := float64(i)
    u := uint(f)
  • type inference

    1
    2
    3
    4
    5
    6
    var i int
    j := i // j is an int
    3
    i := 42 // int
    f := 3.142 // float64
    g := 0.867 + 0.5i // complex128
  • constants

    1
    2
    3
    4
    5
    6
    7
    8
    const Pi = 2.14
    const Truth bool = true
    // numeric constants are high-precision values.
    const (
    Big = 1 << 100
    Small = Big >> 99
    )

Flow control

  • for

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    for i := 0; i < 10; i++ {
    // do something
    }
    for ; i < 10; {
    // do something
    }
    // C's `while` is spelled `for` in Go
    i := 0
    for i < 10 {
    // do something
    }
    // loop forever
    for {
    // do something
    }
  • if

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    if i < 10 {
    // do something
    } else {
    // do something
    }
    if i < 10 {
    // do something
    } else if i < 100 {
    // do something
    }
    // if with a short statement
    // variables declared by the statement are only in scope until the end of the if.
    if v := 1; v < 10 {
    // do something
    }
  • exercise-loops-and-functions.go

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package main
    import (
    "fmt"
    "math"
    )
    func Sqrt(x float64) float64 {
    var cur float64 = x
    for nxt := 0.0; math.Abs(nxt - cur) > 1e-8; {
    t := cur - (cur * cur - x) / (2 * cur)
    nxt = cur
    cur = t
    }
    return cur
    }
    func main() {
    fmt.Println(Sqrt(2))
    }
  • switch

    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
    // a case body breaks automatically, unless it ends with a 'fallthrough' statement.
    fmt.Print("Go runs on ")
    switch os := runtime.GOOS; os {
    case "darwin":
    fmt.Println("OS X.")
    case "linux":
    fmt.Println("Linux.")
    default:
    // freebsd, openbsd,
    // plan9, windows...
    fmt.Printf("%s.", os)
    }
    // switch with no condition
    t := time.Now()
    switch {
    case t.Hour() < 12:
    fmt.Println("Good morning!")
    case t.Hour() < 17:
    fmt.Println("Good afternoon.")
    case t.Hour() < 24:
    fmt.Println("One more")
    fallthrough
    default:
    fmt.Println("Good evening.")
    }
  • defer

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // deferred function calls are pushed onto a stack.
    // when a function returns, its deferred calls are executed in last-in-first-out order.
    func main() {
    fmt.Println("counting")
    for i := 0; i < 3; i++ {
    defer fmt.Println(i)
    }
    fmt.Println("done")
    }
    /*
    counting
    done
    2
    1
    0
    */

More types

  • pointers

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // the type *T is a pointer to a T value.
    // its zero value is nil.
    var p *int
    i := 42
    p = &i
    fmt.Println(*p) // read i through the pointer p
    *p = 21 // set i through the pointer p
  • struct

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    type Vertex struct {
    X int
    Y int
    }
    var v Vertex = Vertex{2, 3} // v := VertexP{2, 3}
    p := &v // pointer to structs
    v.x = 1e9
    p.X = 1e9 // implicit conversion ???
    // struct literals
    var (
    v1 = Vertex{1, 2} // has type Vertex
    v2 = Vertex{X: 1} // Y:0 is implicit
    v3 = Vertex{} // X:0 and Y:0
    p = &Vertex{1, 2} // has type *Vertex
    )
  • array

    1
    var prime [6]int = [6]int{2, 3, 5, 7, 11, 13}
  • slices

    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
    // slices are like references to arrays
    var s []int = primes[1:4]
    // array literal
    [3]bool{true, true, false}
    // slice literal
    []bool{true, true, false}
    // slice defaults
    // the default is zero for the low bound and the length of the slice for the high bound.
    // these slice expressions are equivalent:
    a[0:10]
    a[:10]
    a[0:]
    a[:]
    // nil slices
    // A nil slice has a length and capacity of 0 and has no underlying array.
    var s []int
    fmt.Println(s, len(s), cap(s))
    if s == nil {
    fmt.Println("nil!")
    }
    /*
    [] 0 0
    nil!
    */
  • slice length and capacity

    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
    package main
    import "fmt"
    func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    printSlice(s)
    // Slice the slice to give it zero length.
    s = s[:0]
    printSlice(s)
    // Extend its length.
    s = s[:4]
    printSlice(s)
    // Drop its first two values.
    s = s[2:]
    printSlice(s)
    }
    func printSlice(s []int) {
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
    }
    /*
    len=6 cap=6 [2 3 5 7 11 13]
    len=0 cap=6 []
    len=4 cap=6 [2 3 5 7]
    len=2 cap=4 [5 7]
    */
  • creating a slice with make
    slices can be created with the built-in make function;
    this is how you create dynamically-sized arrays.

    1
    b := make([]int, 0, 5) // len(b)=0, cap(b)=5
  • slices of slices

    1
    2
    3
    4
    5
    6
    7
    8
    board := [][]string{
    []string{"_", "_", "_"},
    []string{"_", "_", "_"},
    []string{"_", "_", "_"},
    }
    for i := 0; i < len(board); i++ {
    fmt.Printf("%s\n", strings.Join(board[i], " "))
    }
  • apending to a slice

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // func append(s []T, vs ...T) []T
    var s []int
    s = append(s, 1)
    s = append(s, 2, 3, 4)
    // append slice
    x := []int{1,2,3}
    y := []int{4,5,6}
    x = append(x, y...)
  • range

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
    for i, v := range pow {
    fmt.Printf("2**%d = %d\n", i, v)
    }
    // skip the index or value
    pow := make([]int, 10)
    for i := range pow {
    pow[i] = 1 << uint(i) // == 2**i
    }
    for _, value := range pow {
    fmt.Printf("%d\n", value)
    }
  • exercise-slices.go

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package main
    import "golang.org/x/tour/pic"
    func Pic(dx, dy int) [][]uint8 {
    var pic [][]uint8 = make([][]uint8, dy)
    for i := 0; i < dy; i++ {
    pic[i] = make([]uint8, dx)
    for j := 0; j < dx; j++ {
    pic[i][j] = uint8(i ^ j);
    }
    }
    return pic
    }
    func main() {
    pic.Show(Pic)
    }
  • maps
    The zero value of a map is nil. A nil map has no keys, nor can keys be added.

    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
    type Vertex struct {
    Lat, Long float64
    }
    var m map[string]Vertex
    m = make(map[string]Vertex)
    m["Bell Labs"] = Vertex{
    40.68433, -74.39967,
    }
    fmt.Println(m["Bell Labs"])
    // map literals
    var m = map[string]Vertex{
    "Bell Labs": Vertex{
    40.68433, -74.39967,
    },
    "Google": Vertex{
    37.42202, -122.08408,
    },
    }
    // If the top-level type is just a type name, you can omit it from the elements of the literal.
    var m = map[string]Vertex{
    "Bell Labs": {40.68433, -74.39967},
    "Google": {37.42202, -122.08408},
    }
    // mutating maps
    // insert or update an element in map m:
    m[key] = elem
    // retrieve an element:
    elem = m[key]
    // delete an element:
    delete(m, key)
    // test that a key is present with a two-value assignment:
    elem, ok = m[key]
  • exercise-maps.go

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package main
    import (
    "golang.org/x/tour/wc";
    "strings"
    )
    func WordCount(s string) map[string]int {
    word := strings.Split(s, " ")
    var mp map[string]int = make(map[string]int)
    for i := 0; i < len(word); i++ {
    mp[word[i]] += 1
    }
    return mp
    }
    func main() {
    wc.Test(WordCount)
    }
  • funtion values

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    func compute(fn func(float64, float64) float64) float64 {
    return fn(3, 4)
    }
    func main() {
    hypot := func(x, y float64) float64 {
    return math.Sqrt(x*x + y*y)
    }
    fmt.Println(hypot(5, 12))
    fmt.Println(compute(hypot))
    fmt.Println(compute(math.Pow))
    }
  • closure
    a closure is a function value that references variables from outside its body.
    the function may access and assign to the referenced variables; in this sense the function is “bound” to the variables.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // each closure is bound to its own sum variable.
    func adder() func(int) int {
    sum := 0
    return func(x int) int {
    sum += x
    return sum
    }
    }
    func main() {
    pos, neg := adder(), adder()
    for i := 0; i < 3; i++ {
    fmt.Println(
    pos(i),
    neg(-2*i),
    )
    }
    }
    /*
    0 0
    1 -2
    3 -6
    */
  • function-closures.go

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package main
    import "fmt"
    // fibonacci is a function that returns
    // a function that returns an int.
    func fibonacci() func() int {
    var f0, f1 int = 0, 1
    return func() int {
    ret := f0
    f0, f1 = f1, f0 + f1
    return ret
    }
    }
    func main() {
    f := fibonacci()
    for i := 0; i < 10; i++ {
    fmt.Println(f())
    }
    }

Methods and interfaces

methods

  • go does not have classes. However, you can define methods on types.
  • a method is a function with a special receiver argument.
  • the receiver appears in its own argument list between the func keyword and the method name
1
2
3
4
5
6
7
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
  • declare a method on non-struct types

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    type Vertex struct {
    X, Y float64
    }
    func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
    }
    func (v *Vertex) Scale(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
    }
    func main() {
    v := Vertex{3, 4}
    v.Scale(10)
    fmt.Println(v.Abs())
    }
  • In general, all methods on a given type should have either value or pointer receivers, but not a mixture of both.

interfaces

an interface type is defined as a set of method signatures.
a value of interface type can hold any value that implements those methods.

  • interfaces are implemented implicitly
    implicit interfaces decouple the definition of an interface from its implementation, which could then appear in any package without prearrangement.

  • interface values
    an interface value holds a value of a specific underlying concrete type.
    it can be thought of as a tuple of a value and a concrete type: (value, type)

  • interface values can be with nil underlying 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
31
32
33
34
35
type I interface {
M()
}
type T struct {
S string
}
// This method means type T implements the interface I,
// but we don't need to explicitly declare that it does so.
func (t T) M() {
if t == nil {
fmt.Println("<nil>")
return
}
fmt.Println(t.S)
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
func main() {
var i I
describe(i) // nil interface value -> run-time error
var t *T
i = t
describe(i) // nil receiver -> OK
i.M()
i = &T{"hello"}
describe(i)
i.M()
}
  • empty interface
    an empty interface may hold values of any type. (every type implements at least zero methods.)
    empty interfaces are used by code that handles values of unknown type.

    1
    2
    3
    var i interface{}
    i = 42
    i = "hello"
  • type assertions

    1
    2
    3
    4
    5
    6
    // if i does not hold a type T, the statement will trigger a panic.
    t := i.(T)
    // if i does not hold a type T, ok will be false
    // and t will be the zero value of type T, and no panic occurs.
    t, ok := i.(T)
  • type switches

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package 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)
    do("hello")
    do(true)
    }
  • Stringers and error

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // a Stringer is a type that can describe itself as a string.
    type Stringer interface {
    String() string
    }
    // the error type is a built-in interface similar to fmt.Stringer
    type error interface {
    Error() string
    }
    // for example, fmt.Println will call the two interfaces
  • exercise-stringer.go

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package main
    import "fmt"
    type IPAddr [4]byte
    // TODO: Add a "String() string" method to IPAddr.
    func (t *IPAddr) String() string {
    return fmt.Sprintf("%v.%v.%v.%v", t[0], t[1], t[2], t[3])
    }
    func main() {
    hosts := map[string]IPAddr{
    "loopback": {127, 0, 0, 1},
    "googleDNS": {8, 8, 8, 8},
    }
    for name, ip := range hosts {
    fmt.Println(ip.String())
    fmt.Printf("%v: %v\n", name, ip)
    }
    }
  • exercise-errors.go

    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
    package main
    import (
    "fmt"
    )
    type ErrNegativeSqrt float64
    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)
    }
    z := 1.0
    for i := 0; i < 10; i++ {
    z = z - (z * z - x) / (2 * z), y
    }
    return z, nil
    }
    func main() {
    fmt.Println(Sqrt(2))
    fmt.Println(Sqrt(-2))
    }
  • readers
    interface io.Reader::Read

    1
    func (T) Read(b []byte) (n int, err error)
  • exercise-reader.go

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package main
    import "golang.org/x/tour/reader"
    type MyReader struct{}
    // TODO: Add a Read([]byte) (int, error) method to MyReader.
    func (t MyReader) Read(b []byte) (int, error) {
    for i := 0; i < len(b); i++ {
    b[i] = 'A'
    }
    return len(b), nil
    }
    func main() {
    reader.Validate(MyReader{})
    }
  • exercise-rot-reader.go

    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
    package main
    import (
    "io"
    "os"
    "strings"
    )
    type rot13Reader struct {
    r io.Reader
    }
    func (r rot13Reader) Read(b []byte) (n int, err error) {
    insideR := r.r
    if n, err = insideR.Read(b); err != nil {
    return
    }
    for i := 0; i < n; i++ {
    if b[i] >= 'a' && b[i] <= 'z' {
    b[i] = (b[i] - 'a' + 13) % 26 + 'a'
    } else if b[i] >= 'A' && b[i] <= 'Z' {
    b[i] = (b[i] - 'A' + 13) % 26 + 'A'
    }
    }
    return
    }
    func main() {
    s := strings.NewReader("Lbh penpxrq gur pbqr!")
    r := rot13Reader{s}
    io.Copy(os.Stdout, &r)
    }
  • exercise-images.go

    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
    package main
    import (
    "golang.org/x/tour/pic"
    "image"
    "image/color"
    )
    /*
    type Image interface {
    ColorModel() color.Model
    Bounds() Rectangle
    At(x, y int) color.Color
    }
    */
    type Image struct{
    Width, Height int
    }
    func (img Image) ColorModel() color.Model {
    return color.RGBAModel
    }
    func (img Image) Bounds() image.Rectangle {
    return image.Rect(0, 0, img.Width, img.Height)
    }
    func (img Image) At(x, y int) color.Color {
    return color.RGBA{uint8(x * y), uint8((x + y) / 2), 100, 255}
    }
    func main() {
    m := Image{100, 100}
    pic.ShowImage(m)
    }

Concurrency

goroutines

A goroutine is a lightweight thread managed by the Go runtime.
go f(x, y, z) starts a new goroutine running f(x, y, z).
The evaluation of f, x, y, and z happens in the current goroutine and the execution of f happens in the new goroutine.
Goroutines run in the same address space, so access to shared memory must be synchronized.

channels

Channels are a typed conduit(pipe) through which you can send and receive values with the channel operator, <-.
By default, sends and receives block until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.

1
2
3
4
5
6
// (The data flows in the direction of the arrow.)
// Like maps and slices, channels must be created before use:
ch := make(chan int)
ch <- v // Send v to channel ch.
v := <-ch // Receive from ch, and
// assign value to v.

buffered Channels

Channels can be buffered. Provide the buffer length as the second argument to make to initialize a buffered channel:
ch := make(chan int, 100)
Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty.

range and close

v, ok := <-ch
ok is false if there are no more values to receive and the channel is closed.
The loop for i := range c receives values from the channel repeatedly until it is closed.

Note: Only the sender should close a channel, never the receiver. Sending on a closed channel will cause a panic.

Another note: Channels aren’t like files; you don’t usually need to close them.
Closing is only necessary when the receiver must be told there are no more values coming, such as to terminate a range loop.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}

select

The select statement lets a goroutine wait on multiple communication operations.
A select blocks until one of its cases can run, then it executes that case.
It chooses one at random if multiple are ready.

Use a default case to try a send or receive without blocking:

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
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
default:
// receiving from c would block
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}

sync.Mutex

We’ve seen how channels are great for communication among goroutines.
But what if we don’t need communication? What if we just want to make sure only one goroutine can access a variable at a time to avoid conflicts?
This concept is called mutual exclusion, and the conventional name for the data structure that provides it is mutex.
Go’s standard library provides mutual exclusion with sync.Mutex and its two methods:
Lock, Unlock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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]
}

  • exercise-equivalent-binary-trees.go

    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
    42
    43
    44
    45
    46
    package main
    import (
    "golang.org/x/tour/tree"
    "fmt"
    )
    // Walk walks the tree t sending all values
    // from the tree to the channel ch.
    func Walk(t *tree.Tree, ch chan int) {
    defer close(ch)
    var walker func(t *tree.Tree)
    walker = func(t *tree.Tree) {
    if t == nil {
    return
    }
    walker(t.Left)
    ch <- t.Value
    walker(t.Right)
    }
    walker(t)
    }
    // Same determines whether the trees
    // t1 and t2 contain the same values.
    func Same(t1, t2 *tree.Tree) bool {
    ch1 := make(chan int)
    ch2 := make(chan int)
    go Walk(t1, ch1)
    go Walk(t2, ch2)
    for x1 := range ch1 {
    x2, ok2 := <-ch2
    if !ok2 || x1 != x2 {
    return false
    }
    }
    return true
    }
    func main() {
    fmt.Println(Same(tree.New(1), tree.New(1)))
    fmt.Println(Same(tree.New(1), tree.New(2)))
    }
  • exercise-web-crawler.go

    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    package main
    import (
    "fmt"
    "sync"
    )
    type Fetcher interface {
    // Fetch returns the body of URL and
    // a slice of URLs found on that page.
    Fetch(url string) (body string, urls []string, err error)
    }
    type Cache struct {
    visited map[string]bool
    mtx sync.Mutex
    }
    type Response struct {
    url string
    body string
    }
    // Crawl uses fetcher to recursively crawl
    // pages starting with url, to a maximum of depth.
    func Crawl(url string, depth int, fetcher Fetcher, result chan Response, cache Cache) {
    // TODO: Fetch URLs in parallel.
    // TODO: Don't fetch the same URL twice.
    // This implementation doesn't do either:
    defer close(result)
    if depth <= 0 {
    return
    }
    cache.mtx.Lock()
    if cache.visited[url] {
    cache.mtx.Unlock()
    return
    }
    cache.visited[url] = true
    cache.mtx.Unlock()
    body, urls, err := fetcher.Fetch(url)
    if err != nil {
    fmt.Println(err)
    return
    }
    result <- Response{url, body}
    // fmt.Printf("found: %s %q\n", url, body)
    for _, u := range urls {
    tempResult := make(chan Response)
    go Crawl(u, depth-1, fetcher, tempResult, cache)
    for re := range tempResult {
    result <- re
    }
    }
    return
    }
    func main() {
    result := make(chan Response)
    go Crawl("http://golang.org/", 4, fetcher, result, Cache{visited:make(map[string]bool)})
    for re := range result {
    fmt.Printf("found: %s %q\n", re.url, re.body)
    }
    }
    // fakeFetcher is Fetcher that returns canned results.
    type fakeFetcher map[string]*fakeResult
    type fakeResult struct {
    body string
    urls []string
    }
    func (f fakeFetcher) Fetch(url string) (string, []string, error) {
    if res, ok := f[url]; ok {
    return res.body, res.urls, nil
    }
    return "", nil, fmt.Errorf("not found: %s", url)
    }
    // fetcher is a populated fakeFetcher.
    var fetcher = fakeFetcher{
    "http://golang.org/": &fakeResult{
    "The Go Programming Language",
    []string{
    "http://golang.org/pkg/",
    "http://golang.org/cmd/",
    },
    },
    "http://golang.org/pkg/": &fakeResult{
    "Packages",
    []string{
    "http://golang.org/",
    "http://golang.org/cmd/",
    "http://golang.org/pkg/fmt/",
    "http://golang.org/pkg/os/",
    },
    },
    "http://golang.org/pkg/fmt/": &fakeResult{
    "Package fmt",
    []string{
    "http://golang.org/",
    "http://golang.org/pkg/",
    },
    },
    "http://golang.org/pkg/os/": &fakeResult{
    "Package os",
    []string{
    "http://golang.org/",
    "http://golang.org/pkg/",
    },
    },
    }

Language specification

Formatting

  • gofmt ?
  • Indentation
    We use tabs for indentation and gofmt emits them by default. Use spaces only if you must.
  • Line length
    Go has no line length limit. Don’t worry about overflowing a punched card. If a line feels too long, wrap it and indent with an extra tab.
  • Parentheses
    Go needs fewer parentheses than C and Java: control structures (if, for, switch) do not have parentheses in their syntax. Also, the operator precedence hierarchy is shorter and clearer, so x<<8 + y<<16 means what the spacing implies, unlike in the other languages.

Commentary

  • Go provides C-style /* */ block comments and C++-style // line comments.
  • Line comments are the norm;
  • Block comments appear mostly as package comments, but are useful within an expression or to disable large swaths of code.

  • Every exported (capitalized) name in a program should have a doc comment.

  • Doc comments work best as complete sentences, which allow a wide variety of automated presentations.
  • The first sentence should be a one-sentence summary that starts with the name being declared.

Names

  • Package names
    By convention, packages are given lower case, single-word names; there should be no need for underscores or mixedCaps. For example, the package in src/encoding/base64 is imported as "encoding/base64" but has name base64, not encoding_base64 and not encodingBase64.
  • Clear and Concise Name
    ring.New
    once.Do
  • Getters and Setters
    If you have a field called owner (lower case, unexported), the getter method should be called Owner (upper case, exported), not GetOwner.

    1
    2
    3
    4
    owner := obj.Owner()
    if owner != user {
    obj.SetOwner(user)
    }
  • Interface names
    By convention, one-method interfaces are named by the method name plus an -er suffix or similar modification to construct an agent noun: Reader, Writer, Formatter, CloseNotifier etc.

  • MixedCaps
    Finally, the convention in Go is to use MixedCaps or mixedCaps rather than underscores to write multiword names.

Semicolons

  • Like C, Go’s formal grammar uses semicolons to terminate statements, but unlike in C, those semicolons do not appear in the source.
  • If the last token before a newline is an identifier, the lexer always inserts a semicolon after the token. This could be summarized as, “if the newline comes after a token that could end a statement, insert a semicolon”.

    1
    2
    // identifiers
    break continue fallthrough return ++ -- ) }
  • One consequence of the semicolon insertion rules is that you cannot put the opening brace of a control structure (if, for, switch, or select) on the next line.
    If you do, a semicolon will be inserted before the brace, which could cause unwanted effects.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // right
    if i < f() {
    g()
    }
    // wrong
    if i < f()
    {
    g()
    }

Redeclaration and reassignment

In a := declaration a variable v may appear even if it has already been declared, provided:

  • this declaration is in the same scope as the existing declaration of v (if v is already declared in an outer scope, the declaration will create a new variable),
  • the corresponding value in the initialization is assignable to v, and
  • there is at least one other variable in the declaration that is being declared a new.
    1
    2
    f, err := os.Open(name)
    d, err := f.Stat()

Data

  • Allocation with new
    new(T) returns a pointer to a newly allocated zero value of type T.
  • Allocation with make
    make(T, args) serves a purpose different from new(T).
    It creates slices, maps, and channels only, and it returns an initialized (not zeroed) value of type T (not *T).
    The reason for the distinction is that these three types represent, under the covers, references to data structures that must be initialized before use.

Arrays

There are major differences between the ways arrays work in Go and C. In Go,

  • Arrays are values. Assigning one array to another copies all the elements.
  • In particular, if you pass an array to a function, it will receive a copy of the array, not a pointer to it.
  • The size of an array is part of its type. The types [10]int and [20]int are distinct.

The value property can be useful but also expensive;
If you want C-like behavior and efficiency, you can pass a pointer to the array.
But even this style isn’t idiomatic Go. Use slices instead.

The blank identifier

The blank identifier can be assigned or declared with any value of any type, with the value discarded harmlessly.

  • Unused imports and variables

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import (
    "fmt"
    "io"
    "log"
    "os"
    )
    var _ = fmt.Printf // For debugging; delete when done.
    var _ io.Reader // For debugging; delete when done.
    func main() {
    fd, err := os.Open("test.go")
    if err != nil {
    log.Fatal(err)
    }
    // TODO: use fd.
    _ = fd
    }
  • Import for side effect
    import _ "net/http/pprof"
    This form of import makes clear that the package is being imported for its side effects, because there is no other possible use of the package: in this file, it doesn’t have a name. (If it did, and we didn’t use that name, the compiler would reject the program.)

  • Interface checks
    If it’s necessary only to ask whether a type implements an interface, without actually using the interface itself, perhaps as part of an error check, use the blank identifier to ignore the type-asserted value:

    1
    2
    3
    if _, ok := val.(json.Marshaler); ok {
    fmt.Printf("value %v of type %T implements json.Marshaler\n", val, val)
    }

One place this situation arises is when it is necessary to guarantee within the package implementing the type that it actually satisfies the interface. If a type—for example, json.RawMessage needs a custom JSON representation, it should implement json.Marshaler, but there are no static conversions that would cause the compiler to verify this automatically. If the type inadvertently fails to satisfy the interface, the JSON encoder will still work, but will not use the custom implementation. To guarantee that the implementation is correct, a global declaration using the blank identifier can be used in the package:

1
var _ json.Marshaler = (*RawMessage)(nil)

Concurrency

concurrency: structuring a program as independently executing components.
parallelism: executing calculations in parallel for efficiency on multiple CPUs.

  • Channels of channels
    // TODO

  • Parallelization
    Either run your job with environment variable GOMAXPROCS set to the number of cores to use or import the runtime package and call runtime.GOMAXPROCS(NCPU).
    A helpful value might be runtime.NumCPU(), which reports the number of logical CPUs on the local machine.

  • A leaky buffer
    // TODO

Errors

  • Panic
    Panic that in effect creates a run-time error that will stop the program.
    It’s always better to let things continue to run rather than taking down the whole program. One possible counterexample is during initialization: if the library truly cannot set itself up, it might be reasonable to panic, so to speak.

    1
    2
    3
    4
    5
    6
    7
    var user = os.Getenv("USER")
    func init() {
    if user == "" {
    panic("no value for $USER")
    }
    }
  • Recover
    When panic is called, including implicitly for run-time errors such as indexing a slice out of bounds or failing a type assertion, it immediately stops execution of the current function and begins unwinding the stack of the goroutine, running any deferred functions along the way.
    If that unwinding reaches the top of the goroutine’s stack, the program dies. However, it is possible to use the built-in function recover to regain control of the goroutine and resume normal execution.
    One application of recover is to shut down a failing goroutine inside a server without killing the other executing goroutines.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    func server(workChan <-chan *Work) {
    for work := range workChan {
    go safelyDo(work)
    }
    }
    func safelyDo(work *Work) {
    defer func() {
    if err := recover(); err != nil {
    log.Println("work failed:", err)
    }
    }()
    do(work)
    }

// TODO: the rest of contents…

A web server

Let’s finish with a complete Go program, a web server.
This one is actually a kind of web re-server. Google provides a service at http://chart.apis.google.com that does automatic formatting of data into charts and graphs.
It’s hard to use interactively, though, because you need to put the data into the URL as a query.
The program here provides a nicer interface to one form of data: given a short piece of text, it calls on the chart server to produce a QR code, a matrix of boxes that encode the text. That image can be grabbed with your cell phone’s camera and interpreted as, for instance, a URL, saving you typing the URL into the phone’s tiny keyboard.
Here’s the complete program.

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
42
43
44
45
46
package main
import (
"flag"
"html/template"
"log"
"net/http"
)
var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18
var templ = template.Must(template.New("qr").Parse(templateStr))
func main() {
flag.Parse()
http.Handle("/", http.HandlerFunc(QR))
err := http.ListenAndServe(*addr, nil)
if err != nil {
log.Fatal("ListenAndServe:", err)
}
}
func QR(w http.ResponseWriter, req *http.Request) {
templ.Execute(w, req.FormValue("s"))
}
const templateStr = `
<html>
<head>
<title>QR Link Generator</title>
</head>
<body>
{{if .}}
<img src="http://chart.apis.google.com/chart?chs=300x300&cht=qr&choe=UTF-8&chl={{.}}" />
<br>
{{.}}
<br>
<br>
{{end}}
<form action="/" name=f method="GET"><input maxLength=1024 size=70
name=s value="" title="Text to QR Encode"><input type=submit
value="Show QR" name=qr>
</form>
</body>
</html>
`

Reference

Go Official Site
A Tour of Go
Effective Go
The Go Programming Language Specification


1. 除非注明,本博文即为原创,转载请注明链接地址
2. 本博文只代表博主当时的观点或结论,请不要恶意攻击
3. 如果本文帮到了您,不妨点一下 下面分享到 按钮,让更多的人看到