DevilKing's blog

冷灯看剑,剑上几分功名?炉香无需计苍生,纵一穿烟逝,万丈云埋,孤阳还照古陵

0%

Go101

原文链接

value parts

The main characteristic of C types is the memory layouts of their values are transparent

Go can also be viewed as C language framework. This is mainly reflected in the fact that Go supports several kinds of types whose value memory layouts are not totally transparent. Each values of the these kinds of types is often composed of one direct part and one or several underlying indirect parts, and the underlying value part is referenced by the direct value part

Two kinds points

  • If a struct value a has a pointer field b which references a value c, then we can say the struct value a also (indirectly) references value c.
  • If a value x references (either directly or indirectly) a value y, and the value y references (either directly or indirectly) a value z, then we can also say the value x (indirectly) references value z

Internal Definitions of the types

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
// map types
type _map *hashtableImpl // currently, for the standard Go compiler,
// Go maps are hashtables actually.

// channel types
type _channel *channelImpl

// function types
type _function *functionImpl

// slice types
type _slice struct {
elements unsafe.Pointer // underlying elements
len int // number of elements
cap int // capacity
}

// string types
type _string struct {
elements *byte // underlying bytes
len int // number of bytes
}

// general interface types
type _interface struct {
dynamicTypeInfo *struct {
dynamicType *_type // the dynamic type
methods []*_function // implemented methods
}
dynamicValue unsafe.Pointer // the dynamic value
}

underlying value parts are not copied in value assignments

In Go, each value assignment (including parameter passing, etc) is a shallow value copy if the involved destination and source values have the same type (if their types are different, we can think that the source value will be implicitly converted to the destination type before doing that assignment). In other words, only the direct part of the soruce value is copied to the destination value in an value assignment. If the source value has underlying value part(s), then the direct parts of the destination and source values will reference the same underlying value part(s), in other words, the destination and source values will share the same underlying value part(s).

Here I just list some absolutely misuses of reference

  • only slice, map, channel and function types are reference types in Go. (If we do need the reference type terminology in Go, then we shouldn’t exclude any custom pointer and pointer wrapper types from reference types).
  • references are opposites of values. (If we do need the reference value terminology in Go, then please view reference values as special values, instead of opposites of values.)
  • some parameters are passed by reference. (Sorry, all parameters are passed by copy in Go.)

line break rules in go

One rule we often obey in practice is, we should not put the a starting curly brace ({) of the explicit code block of a control flow block on a new line.

1
2
for i := 5; i > 0; i-- {
}

However, there are some exceptions for the rule mentioned above. For example, the following bare for loop block compiles okay.

1
2
3
4
for
{
// do something ...
}

活用;

Some Panic/Recover Use Cases

avoid panics crashing programs

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 "errors"
import "log"
import "net"

func main() {
listener, err := net.Listen("tcp", ":12345")
if err != nil {
log.Fatalln(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Println(err)
}
// Handle each client connection in a new goroutine.
go ClientHandler(conn)
}
}

func ClientHandler(c net.Conn) {
defer func() {
if v := recover(); v != nil {
log.Println("client handler panic:", v)
}
c.Close()
}()
panic(errors.New("just a demo.")) // a demo-purpose panic
}

automatically restart a crash goroutine

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 "log"
import "time"

func shouldNotExit() {
for {
time.Sleep(time.Second) // simulate a workload
// Simultate an unexpected panic.
if time.Now().UnixNano() & 0x3 == 0 {
panic("unexpected situation")
}
}
}

func NeverExit(name string, f func()) {
defer func() {
if v := recover(); v != nil { // a panic is detected.
log.Println(name, "is crashed. Restart it now.")
go NeverExit(name, f) // restart
}
}()
f()
}

func main() {
log.SetFlags(0)
go NeverExit("job#A", shouldNotExit)
go NeverExit("job#B", shouldNotExit)
select{} // blocks here for ever
}

use panic/recover to reduce error checkings

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
package main

import "fmt"

func doTask(n int) {
if n%2 != 0 {
// Create a demo-purpose panic.
panic(fmt.Errorf("bad number: %v", n))
}
return
}

func doSomething() (err error) {
defer func() {
// The ok return must be present here, otherwise,
// a panic will be created if no errors occur.
err, _ = recover().(error)
}()

doTask(22)
doTask(98)
doTask(100)
doTask(53)
return nil
}

func main() {
fmt.Println(doSomething()) // bad number: 53
}

close channel

One general principle of using Go channels is don’t close a channel from the receiver side and don’t close a channel if the channel has multiple concurrent senders. In other words, you should only close a channel in a sender goroutine if the sender is the only sender of the channel.