以下内容只是对 A Tour of Go 所做的摘记。有一说一,官方教程是真的好!
package,variable,functions
package && import
Every Go program is made up of packages.
Programs start running in package main.
------------------------------------------------------------------------------------------------
By convention, the package name is the same as the last element of the import path. For instance, the "math/rand" package comprises files that begin with the statement package rand.
import (
"fmt"
"math/rand"
)
func main() {
fmt.Println("My favorite number is", rand.Intn(10))
}
------------------------------------------------------------------------------------------------
This code groups the imports into a parenthesized.
import (
"fmt"
"math"
)
You can also write multiple import statements. And it is good style to use the factored import statement.
import "fmt"
import "math"
------------------------------------------------------------------------------------------------
In Go, a name is exported if it begins with a capital letter. For example, Pi is an exported name, which is exported from the math package. pi do not start with a capital letter, so they are not exported.
When importing a package, you can refer only to its exported names. Any "unexported" names are not accessible from outside the package.
import (
"fmt"
"math"
)
func main() {
fmt.Println(math.pi) // false
fmt.Println(math.Pi) // true
}
function
A function can take zero or more arguments. Notice that the type comes after the variable name.
func add(x int, y int) int {
return x + y
}
When two or more consecutive named function parameters share a type, you can omit the type from all but the last.
func add(x, y int) int {
return x + y
}
A function can return any number of results.
func swap(x, y string) (string, string) {
return y, x
}
Go's return values may be named. If so, they are treated as variables defined at the top of the function. A return statement without arguments returns the named return values. This is known as a "naked" return. Naked return statements should be used only in short functions, as with the example shown here. They can harm readability in longer functions.
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
variable
var c, python, java bool
var c, python, java = true, false, "no!"
c, python, java := true, false, "no!"
:= 只能在函数里面使用,而不能在函数外面使用。
Outside a function, every statement begins with a keyword (var, func, and so on) and so the := construct is not available.
basic types
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.
zero value
The zero value is:
0 for numeric types,
false for the boolean type, and
"" (the empty string) for strings.
type convert
The expression T(v) converts the value v to the type T.
constants
Constants are declared like variables, but with the const keyword.
Constants cannot be declared using the := syntax
const Pi = 3.14
Numeric constants are high-precision values.
An untyped constant takes the type needed by its context.
const (
// Create a huge number by shifting a 1 bit left 100 places.
// In other words, the binary number that is 1 followed by 100 zeroes.
Big = 1 << 100
// Shift it right again 99 places, so we end up with 1<<1, or 2.
Small = Big >> 99
)
func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 {
return x * 0.1
}
func main() {
fmt.Println(needInt(Small))
fmt.Println(needFloat(Small))
fmt.Println(needFloat(Big))
}
Flow control statements
for
Go has only one looping construct, the for loop.
The basic for loop has three components separated by semicolons:
the init statement: executed before the first iteration
the condition expression: evaluated before every iteration
the post statement: executed at the end of every iteration
The init and post statements are optional.
At that point you can drop the semicolons: C's while is spelled for in Go.
If you omit the loop condition it loops forever, so an infinite loop is compactly expressed.
Unlike other languages like C, Java, or JavaScript there are no parentheses surrounding the three components of the for statement and the braces { } are always required.
for i := 0; i < 10; i++ {
}
for ; sum < 1000; {
}
for sum < 1000 {
}
for {
}
if … esle…
the expression need not be surrounded by parentheses( ) but the braces { } are required.
if x < 0 {
return sqrt(-x) + "i"
}
if statement can start with a short statement to execute before the condition. Variables declared by the statement are only in scope until the end of the if.
1. Variables declared inside an if short statement are also available inside any of the else blocks.
if v := math.Pow(x, n); v < lim {
return v
} else {
fmt.Printf("%g >= %g\n", v, lim)
}
switch
switch 中 go 自动提供了 break 语句;case 可以不用是常量也可以不是 integer
In effect, the break statement that is needed at the end of each case in those languages is provided automatically in Go. Another important difference is that Go's switch cases need not be constants, and the values involved need not be integers.
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.\n", os)
}
Switch cases evaluate cases from top to bottom, stopping when a case succeeds.
switch i {
case 0:
case f():
}
// 上面这个例子中,如果 i = 0,那么 f 就不会被调用
Switch without a condition is the same as switch true. This construct can be a clean way to write long if-then-else chains.
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
defer
A defer statement defers the execution of a function until the surrounding function returns.
The deferred call's arguments are evaluated immediately, but the function call is not executed until the surrounding function returns.
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
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 < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}
More types:structs, slices, maps
Go pointer
Go has pointers. A pointer holds the memory address of a value.
Unlike C, Go has no pointer arithmetic.
struct
A struct is a collection of fields.
Struct fields are accessed using a dot.
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
v.X = 4
fmt.Println(v.X)
}
Struct fields can be accessed through a struct pointer. To access the field X of a struct when we have the struct pointer p we could write (*p).X. However, that notation is cumbersome, so the language permits us instead to write just p.X, without the explicit dereference.
func main() {
v := Vertex{1, 2}
p := &v
p.X = 1e9
fmt.Println(v)
}
A struct literal denotes a newly allocated struct value by listing the values of its fields. You can list just a subset of fields by using the Name: syntax. (And the order of named fields is irrelevant.) The special prefix & returns a pointer to the struct value.
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
type [n]T is an array of n values of type T. An array's length is part of its type, so arrays cannot be resized.
slice
An array has a fixed size. A slice, on the other hand, is a dynamically-sized, flexible view into the elements of an array.
The type []T is a slice with elements of type T. A slice is formed by specifying two indices, a low and high bound, separated by a colon: a[low : high]. This selects a half-open range which includes the first element, but excludes the last one.
primes := [6]int{2, 3, 5, 7, 11, 13}
var s []int = primes[1:4]
fmt.Println(s)
------------------------------------------------------------------------------------------------
A slice does not store any data, it just describes a section of an underlying array. Changing the elements of a slice modifies the corresponding elements of its underlying array. Other slices that share the same underlying array will see those changes.
names := [4]string{
"John",
"Paul",
"George",
"Ringo",
}
fmt.Println(names)
a := names[0:2]
b := names[1:3]
fmt.Println(a, b)
b[0] = "XXX"
fmt.Println(a, b)
fmt.Println(names)
------------------------------------------------------------------------------------------------
A slice literal is like an array literal without the length. And this will creates an array, then builds a slice that references it:
[]bool{true, true, false}
------------------------------------------------------------------------------------------------
these slice expressions are equivalent(The default is zero for the low bound and the length of the slice for the high bound):
a[0:10]
a[:10]
a[0:]
a[:]
------------------------------------------------------------------------------------------------
A slice has both a length and a capacity.
The length of a slice is the number of elements it contains.
The capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice.
The length and capacity of a slice s can be obtained using the expressions len(s) and cap(s).
s := []int{2, 3, 5, 7, 11, 13}
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
// Slice the slice to give it zero length.
s = s[:0]
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
// Extend its length.
s = s[:4]
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
// Drop its first two values.
s = s[2:]
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
------------------------------------------------------------------------------------------------
The zero value of a slice is nil. 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!")
}
------------------------------------------------------------------------------------------------
Slices can be created with the built-in make function; this is how you create dynamically-sized arrays.
The make function allocates a zeroed array and returns a slice that refers to that array:
a := make([]int, 5) // len(a)=5,cap(a)=5
b := make([]int, 0, 5) // len(b)=0, cap(b)=5. Third argument is capacity
b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:] // len(b)=4, cap(b)=4
------------------------------------------------------------------------------------------------
Slices can contain any type, including other slices.
board := [][]string{
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
}
------------------------------------------------------------------------------------------------
Go provides a built-in append function to append new elements to a slice.
func append(s []T, vs ...T) []T
The first parameter s of append is a slice of type T, and the rest are T values to append to the slice.
The resulting value of append is a slice containing all the elements of the original slice plus the provided values.
If the backing array of s is too small to fit all the given values a bigger array will be allocated. The returned slice will point to the newly allocated array.
var s []int // len(s)=0, cap(s)=0
s = append(s, 0) // len(s)=1, cap(s)=1
s = append(s, 1) // len(s)=2, cap(s)=2
s = append(s, 2, 3, 4) // len(s)=5, cap(s)=6
range
The range form of the for loop iterates over a slice or map.
When ranging over a slice, two values are returned for each iteration. The first is the index, and the second is a copy of the element at that index.
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
If you only want the index, you can omit the second variable
for i := range pow
You can skip the index or value by assigning to _.
for i, _ := range pow
for _, value := range pow
Map
The zero value of a map is nil. A nil map has no keys, nor can keys be added. The make function returns a map of the given type, initialized and ready for use.
func main() {
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 are like struct literals, but the keys are required.
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},
}
------------------------------------------------------------------------------------------------
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]
Function value
Functions are values too. They can be passed around just like other values. Function values may be used as function arguments and return values.
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))
}
------------------------------------------------------------------------------------------------
Go functions may be closures. 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. For example, the adder function returns a closure. 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 < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
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.
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(v.Abs())
}
------------------------------------------------------------------------------------------------
Remember: a method is just a function with a receiver argument.
func Abs(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(Abs(v))
}
------------------------------------------------------------------------------------------------
You can declare a method on non-struct types, too.
You can only declare a method with a receiver whose type is defined in the same package as the method. You cannot declare a method with a receiver whose type is defined in another package (which includes the built-in types such as int).
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
func main() {
f := MyFloat(-math.Sqrt2)
fmt.Println(f.Abs())
}
------------------------------------------------------------------------------------------------
You can declare methods with pointer receivers. This means the receiver type has the literal syntax *T for some type T. (Also, T cannot itself be a pointer such as *int.) Methods with pointer receivers can modify the value to which the receiver points. With a value receiver, the method operates on a copy of the original value.
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())
}
The functions with a pointer argument must take a pointer. while methods with pointer receivers take either a value or a pointer as the receiver when they are called. As follows, as a convenience, Go interprets the statement v.Scale(5) as (&v).Scale(5) since the Scale method has a pointer receiver.
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
var v Vertex
v.Scale(5) // OK
p := &v
p.Scale(10) // OK
The equivalent thing happens in the reverse direction. Functions that take a value argument must take a value of that specific type. while methods with value receivers take either a value or a pointer as the receiver when they are called. In this case, the method call p.Abs() is interpreted as (*p).Abs().
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
var v Vertex
fmt.Println(v.Abs()) // OK
p := &v
fmt.Println(p.Abs()) // OK
------------------------------------------------------------------------------------------------
There are two reasons to use a pointer receiver.
The first is so that the method can modify the value that its receiver points to.
The second is to avoid copying the value on each method call. This can be more efficient if the receiver is a large struct, for example.
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.
A type implements an interface by implementing its methods. There is no explicit declaration of intent, no "implements" keyword.
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() {
fmt.Println(t.S)
}
------------------------------------------------------------------------------------------------
Interface values can be thought of as a tuple of a value and a concrete type: (value, type). Calling a method on an interface value executes the method of the same name on its underlying type.
type I interface {
M()
}
type T struct {
S string
}
func (t *T) M() {
fmt.Println(t.S)
}
type F float64
func (f F) M() {
fmt.Println(f)
}
func main() {
var i I
i = &T{"Hello"}
describe(i)
i.M()
i = F(-math.Pi)
describe(i)
i.M()
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
------------------------------------------------------------------------------------------------
If the concrete value inside the interface itself is nil, the method will be called with a nil receiver. In some languages this would trigger a null pointer exception, but in Go it is common to write methods that gracefully handle being called with a nil receiver.
type I interface {
M()
}
type T struct {
S string
}
func (t *T) M() {
if t == nil {
fmt.Println("<nil>")
return
}
fmt.Println(t.S)
}
func main() {
var i I
var t *T
i = t
describe(i)
i.M()
i = &T{"hello"}
describe(i)
i.M()
}
A nil interface value holds neither value nor concrete type. Calling a method on a nil interface is a run-time error because there is no type inside the interface tuple to indicate which concrete method to call.
type I interface {
M()
}
func main() {
var i I
describe(i)
i.M()
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
// 也就是说是根据 type 调用相应的方法,再把相应的 value 传进去
------------------------------------------------------------------------------------------------
The interface type that specifies zero methods is known as the empty interface: interface{}. An empty interface may hold values of any type. Empty interfaces are used by code that handles values of unknown type. For example, fmt.Print takes any number of arguments of type interface{}.
assertions
A type assertion provides access to an interface value's underlying concrete value. This statement asserts that the interface value i holds the concrete type T and assigns the underlying T value to the variable t. If i does not hold a T, the statement will trigger a panic.
t := i.(T).
To test whether an interface value holds a specific type, a type assertion can return two values: the underlying value and a boolean value that reports whether the assertion succeeded. If i holds a T, then t will be the underlying value and ok will be true. If not, ok will be false and t will be the zero value of type T, and no panic occurs.
t, ok := i.(T)
------------------------------------------------------------------------------------------------
A type switch is like a regular switch statement, but the cases in a type switch specify types (not values), and those values are compared against the type of the value held by the given interface value.
switch v := i.(type) { // type is a keyword
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)
}
error
The error type is a built-in interface similar to fmt.Stringer:
type error interface {
Error() string
}
As with fmt.Stringer, the fmt package looks for the error interface when printing values.