The Go Programming Language 笔记

Chapter 1

1.2

i++ is statement, so j = i++ is illegal

s := "" can be only used within a function

Package-level variables are initialized before main begins (§2.6.2), and local variables are initialized as their declarations are encountered during function execution

Since new is a predeclared function, not a key word, it’s possible to redefine the name for something else within a function

Chapter 2

2.3

The zero value of an aggregate type like an array or a struct has the zero value of all of its elements or fields.
The zero-value me chanism ensures that a variable always holds a well-define d value of its type; in Go there is no such thing as an uninitialized variable.

Keep in mind that := is a declaration, whereas = is an assignment

A short variable declaration must declare at least one new variable, however, so this code will not compile:

1
2
3
f, err := os.Open(infile)
// ...
f, err := os.Create(outfile) // compile error: no new variables

Each call to new returns a distinct variable with aunique address:

1
2
3
p := new(int)
q := new(int)
fmt.Println(p == q) // "false"

There is one except ion to this rule: two variables whose type carries no infor mation and is therefore of size zero, such as struct{} or [0]int, may, depending on the implement ation, have the same address.

Chapter 3

Although Go provides unsigned numbers and arithmetic, we tend to use the signed int form even for quantities that can’t be negative, such as the length of an array, though uint might seem a more obvious choice. Indeed, the built-in len function returns a signed int, as in this loop which announces prize medals in reverse order:

1
2
3
4
     medals := []string{"gold", "silver", "bronze"}
for i := len(medals) - 1; i >= 0; i-- {
fmt.Println(medals[i]) // "bronze", "silver", "gold"
}

The alternative would be calamitous. If len returned an unsigned number, then i too would be a uint, and the condition i >= 0 would always be true by definition. After the third itera- tion, in which i == 0, the i– statement would cause i to become not −1, but the maximum uint value (for example, 264−1), and the evaluation of medals[i] would fail at run time, or panic (§5.9), by attempting to access an element outside the bounds of the slice

Chapter 4

The size of an array is part of its type, so [3]int and [4]int are different types.

If an array’s element type is comparable then the array type is comparable too, so we may directly compare two arrays of that type using the == operator, which reports whether all corresponding elements are equal. The != operator is its negation.

1
2
3
4
5
6
a := [2]int{1, 2}
b := [...]int{1, 2}
c := [2]int{1, 3}
fmt.Println(a == b, a == c, b == c) // "true false false"
d := [3]int{1, 2}
fmt.Println(a == d) // compile error: cannot compare [2]int == [3]int

Unlike arrays, slices are not comparable, so we cannot use == to test whether two slices contain the same elements. The standard library provides the highly optimized bytes.Equal function for comparing two slices of bytes ([]byte), but for other types of slice, we must do the comparison ourselves

4.3 Maps

In Go, a map is a reference to a hash table, and a map type is written map[K]V, where K and V are the types of its keys and values. All of the keys in a given map are of the same type, and all of the values are of the same type, but the keys need not be of the same type as the values. The key type K must be comparable using ==, so that the map can test whether a given key is equal to one already within it. Though floating-point numbers are comparable, it’s a bad idea to compare floats for equality and, as we mentioned in Chapter 3, especially bad if NaN is a possible value. There are no restrictions on the value type V.

a map element is not a variable, and we cannot take its address:

1
_ = &ages["bob"] // compile error: cannot take address of map element

4.4 structs

1
2
3
4
5
6
7
8
9
type Employee struct {
ID int
Name string
Address string
DoB time.Time
Position string
Salary int
ManagerID int
}

Field order is significant to type identity. Had we also combined the declaration of the Position field (also a string), or interchanged Name and Address, we would be defining a different struct type.

Nor can you use the (order-based) first form of literal to sneak around the rule that unexported identifiers may not be referred to from another package.

1
2
3
4
5
6
package p
type T struct{ a, b int } // a and b are not exported
package q
import "p"
var _ = p.T{a: 1, b: 2} // compile error: can't reference a, b
var _ = p.T{1, 2} // compile error: can't reference a, b

Although the last line above doesn’t mention the unexported field identifiers, it’s really using them implicitly, so it’s not allowed.

4.4.2 Comparing Structs

If all the fields of a struct are comparable, the struct itself is comparable, so two expressions of that type may be compared using == or !=.
Comparable struct types, like other comparable types, may be used as the key type of a map.

1
2
3
4
5
6
     type address struct {
hostname string
port int
}
hits := make(map[address]int)
hits[address{"golang.org", 443}]++

Chapter 5

5.3. Multiple Return Values

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func findLinks(url string) ([]string, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return nil, fmt.Errorf("getting %s: %s", url, resp.Status)
}
doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
return nil, fmt.Errorf("parsing %s as HTML: %v", url, err)
}
return visit(nil, doc), nil
}

We must ensure that resp.Body is closed so that network resources are properly released even in case of error. Go’s garbage collector recycles unused memory, but do not assume it will release unused operating system resources like open files and network connections. They should be closed explicitly

Chapter 6

Named types (Point) and pointers to them (*Point) are the only types that may appear in a receiver declaration. Furthermore, to avoid ambiguities, method declarations are not permit- ted on named types that are themselves pointer types:

1
2
type P *int
func (P) f() { /* ... */ } // compile error: invalid receiver type

Chapter 8

8.4.1

A send operation on an unbuffered channel blocks the sending goroutine until another goroutine executes a corresponding receive on the same channel, at which point the value is transmitted and both goroutines may continue. Conversely, if the receive operation was attempted first, the receiving goroutine is blocked until another goroutine performs a send on the same channel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func main() {
worklist := make(chan []string)
// Start with the command-line arguments.
go func() { worklist <- os.Args[1:] }()
// Crawl the web concurrently.
seen := make(map[string]bool)
for list := range worklist {
for _, link := range list {
if !seen[link] {
seen[link] = true
go func(link string) {
worklist <- crawl(link)
}(link)
}
}
}
}

Notice that the crawl goroutine takes link as an explicit parameter to avoid the problem of loop variable capture we first saw in Section 5.6.1. Also notice that the initial send of the com- mand-line arguments to the worklist must run in its own goroutine to avoid deadlock, a stuck situation in which both the main goroutine and a crawler goroutine attempt to send to each other while neither is receiving.

1
2
3
4
5
6
7
8
func mirroredQuery() string {
responses := make(chan string, 3)
go func() { responses <- request("asia.gopl.io") }()
go func() { responses <- request("europe.gopl.io") }()
go func() { responses <- request("americas.gopl.io") }()
return <-responses // return the quickest response
}
func request(hostname string) (response string) { /* ... */ }

Had we used an unbuffered channel, the two slower goroutines would have gotten stuck trying to send their responses on a channel from which no goroutine will ever receive. This situation, called a goroutine leak, would be a bug. Unlike garbage variables, leaked goroutines are not automatically collected, so it is important to make sure that goroutines terminate themselves when no longer needed.

Failure to allocate sufficient buffer capacity would cause the program to deadlock.

Chapter 9

p261
There are three ways to avoid a data race:
The first way is not to write the variable.
The second way to avoid a data race is to avoid accessing the variable from multiple goroutines.
The third way to avoid a data race is to allow many goroutines to access the variable, but only one at a time. This approach is known as mutual exclusion