The proposed learning path:
GO language tour
GO official documents
Labeled examples
Book: The GO Programming Language
Book: Go in action
basis
Packages, variables, and functions
Package: according to the Convention, the package name is consistent with the last element of the import path. For example, the source code in the "math/rand" package starts with the package rand statement.
Import
Export name: in Go, if a name begins with a capital letter, it is exported.
Function: if the type is after the variable name, func add (x, y int) int {return x + y}
Multi value return: func swap (x, y string) (string, string) {return y, X}
Named return value: a return statement without parameters returns a named return value. That is, return directly.
Variables: var statements can appear at the package or function level.
Variable initialization: var c, python, java = true, false, "no!"
Short variable declaration: every statement outside a function must start with a keyword (var, func, etc.), so the = structure cannot be used outside a function.
Basic types: int, uint, and uintptr are typically 32-bit wide on 32-bit systems and 64 bit wide on 64 bit systems.
Numeric constants: numeric constants are high-precision values.
A constant of an unspecified type is typed by context.
1<<100:max, moving left again will overflow
Process control statements: for, if, else, switch, and defer
Loop only has for
for i := 0; i < 10; i++ { sum += i }
For can have no initial conditions and post statements. In this case, for is while
for sum<1000{sum+=sum}
if: like for, an if statement can execute a simple statement before a conditional expression.
The variable scope declared by this statement is only within if.
switch:Go automatically provides the required break statements after each case in these languages. The branch terminates automatically unless it ends with a fallthrough statement.
Switch without conditions is the same as switch true.
switch { case t.Hour() < 12: fmt.Println("Good morning!") case t.Hour() < 17: fmt.Println("Good afternoon.") default: fmt.Println("Good evening.") }
defer: postpones the execution of the function until the outer function returns. The parameters of a deferred function are evaluated immediately, but the function is not called until the outer function returns.
Close file stream, lock and printFooter
delay The function call of is pushed onto a stack. When the outer function returns, the deferred function will be called in the order of last in first out.
Three rules:
- A deferred function's arguments are evaluated when the defer statement is evaluated eager evaluation
- Deferred function calls are executed in Last In First Out order after the surrounding function returns First in, last out
- Deferred functions may read and assign to the returning function's named return values The function of defer can modify the return value. Function: decorate error results
func c() (i int) { defer func() { i++ }() return 1 }
Defer related: Panic and Recover
Panic:When the function F calls panic, execution of F stops, any deferred functions in F are executed normally, and then F returns to its caller.
Recover:If the current goroutine is panicking, a call to recover will capture the value given to panic and resume normal execution.
package main import "fmt" func main() { f() fmt.Println("Returned normally from f.") } func f() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered in f", r) } }() fmt.Println("Calling g.") g(0) fmt.Println("Returned normally from g.") } func g(i int) { if i > 3 { fmt.Println("Panicking!") panic(fmt.Sprintf("%v", i)) } defer fmt.Println("Defer in g", i) fmt.Println("Printing in g", i) g(i + 1) }
output
Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Recovered in f 4
Returned normally from f.
More types: Struct, slice, and mapping
Pointer: unlike C, the Go pointer has no operation. GO functions pass values map means that GO saves pointer operation through packaging and still transfers values. map==*hmap, chan=*hchan, and slice also imitate reference types.
A structure is a set of fields
type Vertex struct { X int Y int } fmt.Println(Vertex{1,2}) //{1 2} v:=Vertex{1,2} v.X=2 p:=&v p.X=1e9 //p.X is (*p) X
Array: the length of an array is part of its type, so the array cannot be resized.
var a [2]string
Slicing: slicing provides a dynamically sized, flexible view of array elements.
Changing the element of a slice modifies the corresponding element in its underlying array.
These changes are observed by the slices that share the underlying array with it.
Compare array grammar to slice Grammar:
Array:
[3]bool{true, true, false}
Slice: create an identical array and then build a slice of it
[]bool{true, true, false}
The length of the slice is the number of elements it contains.
The capacity of a slice is the number from its first element to the end of its underlying array element.
Nil slice: the zero value of slice is nil.
nil slices have a length and capacity of 0 and no underlying array.
make create slice: generate an array whose element is 0 and return its slice
B: = make ([]int, 0, 5) / / type, length, capacity
Slice of slice: a slice can contain any type, even its slice.
Append element to slice: s=append(s,2,3)
GO slice: usage and essence
The array of Go is value semantics. An array variable represents the entire array. It is not a pointer to the first element (unlike C language arrays). (Understanding: the pointer to the array points to the entire array. The array is a huge structure in which the elements are its members)
The slice operation does not copy the element pointed to by the slice. It creates a new slice and reuses the underlying array of the original slice.
a = append(a, b...) Slice expansion as element
Range: used to traverse slices or maps. When you use the for loop to iterate through slices, each iteration returns two values. The first value is the index of the current element, and the second value is a copy of the element corresponding to the index.
Only index: for I: = range pow
map: mapping
m:=make(map[string]Vertex) //initialization m[key] = elem //assignment elem = m[key] //obtain elem, ok := m[key] //Obtain, check whether there is
function value
func compute(fn func(float64, float64) float64) float64 { return fn(3, 4) //The parameter is a func. Execute func(3,4) and return float64 }
Closure: closed functions will reference values outside the function body, which can be modified. Closures can reuse local variables without polluting the global.
- Outer function nested inner function
- Inner functions use local variables of outer functions
- Take the inner function as the return value of the outer function
func adder() func(int) int { //The return value is a function: the function receives int and returns int sum := 0 return func(x int) int { sum += x return sum } } func main() { pos, neg := adder(), adder() for i := 0; i < 10; i++ { fmt.Println( pos(i), neg(-2*i), ) } } 0 0 1 -2 3 -6 6 -12 10 -20 15 -30 21 -42 28 -56 36 -72 45 -90
package main import "fmt" func fibonacci() func() int { x1 := 0 x2 := 1 return func() int { defer func() { x1 += x2 x1, x2 = x2, x1 }() return x1 } } func main() { f := fibonacci() for i := 0; i < 10; i++ { fmt.Println(f()) } }
Methods and interfaces
Method: Go has no class. However, you can define methods for structure types. Methods are functions with special receiver parameters.
func (v Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) }
The receiver's type definition and method declaration must be in the same package; Methods cannot be declared for built-in types.
The type of receiver can use the grammar of *T. Because a method often needs to modify its receiver, the pointer receiver is more commonly used than the receiver.
Methods and pointer redirection: functions with pointer parameters must accept a pointer; When a method with a pointer as the receiver is called, the receiver can be both a value and a pointer; Go interprets the statement v.Scale(5) as & v Scale (5).
Why use pointer recipients:
- Method can modify the value pointed to by its receiver
- To avoid copying the value every time the method is called
Interface: a collection of method signature definitions.
Implicit implementation of interface:
type I interface { M() } type T struct { S string } // This method means that type T implements interface I, but we do not need to explicitly declare this. func (t T) M() { fmt.Println(t.S) } func main() { var i I = T{"hello"} i.M() }
Interface value with the underlying value of nil: handle it gracefully
func (t *T) M() { if t == nil { fmt.Println("<nil>") return } fmt.Println(t.S) }
nil interface value:
Calling a method for a nil interface generates a runtime error because the tuple of the interface does not contain a type that can indicate which specific method to call.
Empty interface: an empty interface can store values of any type. (because each type implements at least zero methods.)
Type assertion: provides a way to access the underlying concrete values of interface values.
t, ok := i.(T)
Type selection
Select the structure of branches from several type assertions in order
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) } /*Twice 21 is 42 "hello" is 5 bytes long I don't know about type bool!*/
Stringer
type Stringer interface { String() string }
func (p Person) String() string { return fmt.Sprintf("%v (%v years)", p.Name, p.Age) }
func (ip IPAddr) String() string { return fmt.Sprintf("%v.%v.%v.%v", ip[0], ip[1], ip[2], ip[3]) }
Error
package main import ( "fmt" "math" ) type ErrNegativeSqrt float64 func (e ErrNegativeSqrt) Error() string { return fmt.Sprintf("cannot Sqrt negative number: %v\n",float64(e)) } func Sqrt(x float64) (float64, error) { if x >= 0 { return math.Sqrt(x), nil } else { return 0, ErrNegativeSqrt(x) } } func main() { fmt.Println(Sqrt(2)) fmt.Println(Sqrt(-2)) }
io.Reader
func (T) Read(b []byte) (n int, err error) //Read fills a given byte slice with data and returns the number of bytes filled and the error value.
Example:
package main import ( "io" "os" "strings" ) type rot13Reader struct { r io.Reader } func rot13(out byte) byte { //Letter conversion switch { case out >= 'A' && out <= 'M' || out >= 'a' && out <= 'm': out += 13 case out >= 'N' && out <= 'Z' || out >= 'n' && out <= 'z': out -= 13 } return out } func (f rot13Reader) Read(b []byte) (int, error) { n, err := f.r.Read(b) for i:=0;i<n;i++{ b[i]=rot13(b[i]) } return n, err } func main() { s := strings.NewReader("Lbh penpxrq gur pbqr!") r := rot13Reader{s} io.Copy(os.Stdout, &r) }
Concurrency
Goroutine: a lightweight thread managed by the go runtime.
Channel: a pipe with a type through which values can be sent or received using the channel operator < -.
ch := make(chan int) //Create a channel. ch <- v // Send v to channel ch. v := <-ch // Receive the value from ch and assign v.
Buffered channel: ch: = make (Chan int, 100)
Channel shutdown
Sender close: close(c)
Receiver test: V, OK: = <-ch
Note: only the sender can close the channel, while the receiver cannot. Sending data to a closed channel can cause a panic.
Also note that channels are different from files and do not normally need to be turned off. It is necessary to close only when the receiver must be told that there is no longer a value to send, such as terminating a range loop.
select statement: enables a Go procedure to wait for multiple communication operations
package main import "fmt" 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 } } } 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) }
select can add default
Exercise: equivalent search Binary Tree
package main import "golang.org/x/tour/tree" import "fmt" // Walk step tree t sends all values from the tree to channel ch. func Walk(t *tree.Tree, ch chan int){ for i:=range(t.String()){ ch<-i } close(ch) } // The Same checks whether the trees t1 and t2 contain the Same value. func Same(t1, t2 *tree.Tree) bool{ ch1:=make(chan int) ch2:=make(chan int) go Walk(t1,ch1) go Walk(t2,ch2) var v2 int var ok2 bool for v1,ok:=<-ch1;ok==true;v1,ok=<-ch1{ v2,ok2=<-ch2 if ok2==false || v1!=v2{ return false } } v2,ok2=<-ch2 if ok2==true{ return false } return true } func main() { if Same(tree.New(1), tree.New(2)){ fmt.Println("Same.") }else { fmt.Println("Different.") } }
The last two exercises: sync Mutex, Web crawler, noticed that the multi thread needs to wait for the end to use sync.Waitgroup , which ensures that all threads complete before exiting
Comparison between waitgroup and official channel Click here Note that the channel marked here does not conform to the GO language style:
Channel
passing ownership of data,
distributing units of work,
communicating async results
Mutex
caches,
state