golang语法详解笔记

Google Go语言 golang 语法详解笔记

===

===

包 Package

包的声明 Declare

    package cxy     // 声明一个名为“cxy”的包

    package 我的包   // 声明一个名为“我的包”的包

    package main    // main包, 程序启动执行的入口包

错误的包声明

    package "mypkg" // 错误

    package a/b/c   // 错误

    pakcage a.b.c   // 错误

包的导入 Import

    // 导入$GOROOT/$GOOS_$GOARCH/中的相对路径包(官方标准库)
    import "fmt"
    import "math/rand"

    // 导入$GOPATH/$GOOS_$GOARCH/中的相对路径包
    import "github.com/user/project/pkg"
    import "code.google.com/p/project/pkg"

导入当前包的相对路径包
例如有Go目录如下:
$GOPATH/src
 ├─x0
 │ ├─y0
 │ │ └─z0
 │ └─y1
 │  └─z1
 └─x1
  └─y2

    import "./y0/z0"    // x0包中导入子包 z0包
    import "../y0/z0"   // y1包中导入子包 z0包
    import "x0/y1/z1"   // y2包中导入 z1包

错误的导入包路径

    import a/b/c        // 错误
    import "a.b.c"      // 错误
    import a.b.c        // 错误
    import ("fmt"; "math")

    import (
        "fmt"
        "math"
    )
    import (
        "a/b/c"

        c1 "x/y/c"     // 将导入的包c定义别名为 c1

        格式化 "fmt"    // 将导入的包fmt定义别名为 格式化

        m "math"       // 将导入的包math定义别名为 m
    )
    // 引用普通名称的导入包
    c.hello()

    // 引用定义别名的包
    格式化.Println(m.Pi)

定义的包名与所在目录名称不同时,导入包路径仍为目录所在路径,引用包名为定义的包名称

    // 源文件路径: $GOPATH/src/proj/my-util/util.go
    // 定义包名: util
    package util
    // 导入util包路径
    import "proj/my-util"

    // 引用util包
    util.doSomething()
    // 类似C中的include 或Java中的import static
    import . "fmt"

    // 然后像使用本包元素一样使用fmt包中可见的元素,不需要通过包名引用
    Println("no need package name")
    // 如果当前go源文件中未引用过log包,将会导致编译错误
    import "log"    // 错误
    import . "log"  // 静态导入未使用同样报错

    // 在包名前面增加下划线表示导入包但是不直接使用它,被导入的包中的init函数会在导入的时候执行
    import _ "github.com/go-sql-driver/mysql"

包内元素的可见性 Accessability

    var In int                                      // In is exported
    var in byte                                     // in is unexported
    var ȸȹ string                                   // ȸȹ is unexported
    const Ȼom bool = false                          // Ȼom is exported
    const ѧѩ uint8 = 1                             // ѧѩ is unexported
    type Ĩnteger int                                // Ĩnteger is exported
    type ブーリアン *bool                             // ブーリアン is unexported
    func Ӭxport() {...}                              // Ӭxport is exported
    func įnner() {...}                               // įnner is unexported
    func (me *Integer) ⱱalueOf(s string) int {...}   // ⱱalueOf is unexported
    func (i ブーリアン) Ȿtring() string {...}         // Ȿtring is exported
    package pdf // import "rsc.io/pdf"

如果使用此包的代码的导入的路径不是规范路径,go命令会拒绝编译。
例如有 rsc.io/pdf 的一个fork路径 github.com/rsc/pdf
如下程序代码导入路径时使用了非规范的路径则会被go拒绝编译

    import "github.com/rsc/pdf"

数据类型 Data Type

基础数据类型 Basic data type

变量 Variable

    var i int       // i = 0

    var s string    // s = ""    (Go中的string是值类型,默认零值是空串 "" 或 ``,不存在nil(null)值)

    var e error     // e = nil, error是Go的内建接口类型。

关键字的顺序错误或缺少都是编译错误的

    var int a       // 编译错误
    a int           // 编译错误
    int a           // 编译错误
    var a,b,c int   // a = 0, b = 0, c = 0
    var (
        a int       // a = 0
        b string    // b = ""
        c uint      // c = 0
    )
    var (
        a,b,c int
        d string
    )
    var a int = 0
    var a, b int = 0, 1
    var a = 'A'         // a int32
    var a,b = 0, "B"    // a int, b string
    a := 3                     // a int
    a, b, c := 8, '呴', true   // a int, b int32, c bool
    c := `formatted
     string`                   // c string
    c := 1 + 2i                // c complex128

常量 Constant

    const x int = 3
    const y,z int = 1,2
    const (
        a byte = 'A'
        b string = "B"
        c bool = true
        d int = 4
        e float32 = 5.1
        f complex64 = 6 + 6i
    )
    const a = 0        // a int
    const (
        b = 2.3        // b float64
        c = true       // c bool
    )
    const (
        a = 3               // a = 3
        b                   // b = 3
        c                   // c = 3
        d = len("asdf")     // d = 4
        e                   // e = 4
        f                   // f = 4
        g,h,i = 7,8,9       // 复用表达式要一一对应
        x,y,z               // x = 7, y = 8, z = 9
    )
    const a int = iota        // a = 0
    const b int = iota        // b = 0
    const c byte = iota       // c = 0
    const d uint64 = iota     // d = 0
    const (
        a uint8 = iota        // a = 0
        b int16 = iota        // b = 1
        c rune = iota         // c = 2
        d float64 = iota      // d = 3
        e uintptr = iota      // e = 4
    )
    const (
        a = "A"
        b = 'B'
        c = iota    // c = 2
        d = "D"
        e = iota    // e = 4
    )
    const (
        a = iota     // a int32 = 0
        b            // b int32 = 1
        c            // c int32 = 2
    )
    const (
        a byte = iota    // a uint8 = 0
        b                // b uint8 = 1
        c                // c uint8 = 2
        d rune = iota    // d int32 = 3
        e                // e int32 = 4
        f                // f int32 = 5
    )
    const (
        a = iota     // a int32 = 0
        b            // b int32 = 1
        c            // c int32 = 2
    )
    const (
        e = iota     // e int32 = 0    (iota重新初始化并自增)
        f            // f int32 = 1
    )
    const (
        a = (iota + 2) * 3    // a int32 = 6    (a=(0+2)*3) 初始值为6,步进值为3
        b                     // b int32 = 9    (b=(1+2)*3)
        c                     // c int32 = 12    (c=(2+2)*3)
        d                     // d int32 = 15    (d=(3+2)*3)
    )

数组 Array

    var a [3]int = [3]int{0, 1, 2}                         // a = [0 1 2]
    var b [3]int = [3]int{}                                // b = [0 0 0]
    var c [3]int
    c = [3]int{}
    c = [3]int{0,0,0}                                      // c = [0 0 0]
    d := [3]int{}                                          // d = [0 0 0]
    fmt.Printf("%T\t%#v\t%d\t%d\n", d, d, len(d), cap(d))  // [3]int    [3]int{0, 0, 0}    3    3

使用...自动计算数组的长度

    var a = [...]int{0, 1, 2}

    // 多维数组只能自动计算最外围数组长度
    // 因为示例在blog中引起语法错误, 无法展示示例
    // Hi, 猜猜怎么用?

    // 通过下标访问数组元素
    println(y[1][1][0])                                    // 6

初始化指定索引的数组元素,未指定初始化的元素保持默认零值

    var a = [3]int{2:3}
    var b = [...]string{2:"c", 3:"d"}

切片 Slice

    var a []int
    fmt.Printf("%T\t%#v\t%d\t%d\n", a, a, len(a), cap(a))    // []int    []int(nil)    0    0

    // 可用类似数组的方式初始化slice
    var d []int = []int{0, 1, 2}
    fmt.Printf("%T\t%#v\t%d\t%d\n", d, d, len(d), cap(d))    // []int    []int{0, 1, 2}    3    3

    var e = []string{2:"c", 3:"d"}

使用内置函数make初始化slice,第一参数是slice类型,第二参数是长度,第三参数是容量(省略时与长度相同)

    var b = make([]int, 0)
    fmt.Printf("%T\t%#v\t%d\t%d\n", b, b, len(b), cap(b))    // []int    []int{}    0    0

    var c = make([]int, 3, 10)
    fmt.Printf("%T\t%#v\t%d\t%d\n", c, c, len(c), cap(c))    // []int    []int{}    3    10

    var a = new([]int)
    fmt.Printf("%T\t%#v\t%d\t%d\n", a, a, len(*a), cap(*a))  // *[]int    &[]int(nil)    0    0
    s := []int{0, 1, 2, 3, 4}
    a := s[1:3]            // a: [1 2],  len: 2,  cap:  4
    b := s[:4]             // b: [0 1 2 3],  len: 4,  cap:  5
    c := s[1:]             // c: [1 2 3 4],  len: 4,  cap:  4
    d := s[1:1]            // d: [],  len: 0,  cap:  4
    e := s[:]              // e: [0 1 2 3 4],  len: 5,  cap:  5

3个参数 slice[beginIndex:endIndex:capIndex]
需要满足条件:0 <= beginIndex <= endIndex <= capIndex <= cap(slice)
新slice的长度:length=(endIndex - beginIndex)
新slice的容量:capacity=(capIndex - beginIndex)
beginIndex的值可省略,默认为0

    s := make([]int, 5, 10)
    a := s[9:10:10]        // a: [0],  len: 1,  cap:  1
    b := s[:3:5]           // b: [0 0 0],  len: 3,  cap:  5
    s := []string{}
    s = append(s, "a")              // 添加一个元素
    s = append(s, "b", "c", "d")    // 添加一列元素
    t = []string{"e", "f", "g"}
    s = append(s, t...}             // 添加另一个切片t的所有元素
    s = append(s, t[:2]...}         // 添加另一个切片t的部分元素

    s[0] = "A"                      // 修改切片s的第一个元素
    s[len(s)-1] = "G"               // 修改切片s的最后一个元素
    func deleteByAppend() {
        i := 3
        s := []int{1, 2, 3, 4, 5, 6, 7}
        //delete the fourth element(index is 3), using append
        s = append(s[:i], s[i+1:]...)
    }

    func deleteByCopy() {
        i := 3
        s := []int{1, 2, 3, 4, 5, 6, 7}
        //delete the fourth element(index is 3), using copy
        copy(s[i:], s[i+1:])
        s = s[:len(s)-1]
    }

字典/映射 Map

    var m map[int]int
    m[0] = 0                              // × runtime error: assignment to entry in nil map
    fmt.Printf("type: %T\n", m)           // map[int]int
    fmt.Printf("value: %#v\n", m)         // map[int]int(nil)
    fmt.Printf("value: %v\n", m)          // map[]
    fmt.Println("is nil: ", nil == m)     // true
    fmt.Println("length: ", len(m))       // 0,if m is nil, len(m) is zero.

使用内置函数make初始化map

    var m map[int]int = make(map[int]int)
    m[0] = 0                              // 插入或修改元素
    fmt.Printf("type: %T\n", m)           // map[int]int
    fmt.Printf("value: %#v\n", m)         // map[int]int(0:0)
    fmt.Printf("value: %v\n", m)          // map[0:0]
    fmt.Println("is nil: ", nil == m)     // false
    fmt.Println("length: ", len(m))       // 1

直接赋值初始化map

    m := map[int]int{
    0:0,
    1:1,                                  // 最后的逗号是必须的
    }
    n := map[string]S{
    "a":S{0,1},
    "b":{2,3},                            // 类型名称可省略
    }

map的使用:读取、添加、修改、删除元素

    m[0] = 3                              // 修改m中key为0的值为3
    m[4] = 8                              // 添加到m中key为4值为8

    a := n["a"]                           // 获取n中key为“a“的值
    b, ok := n["c"]                       // 取值, 并通过ok(bool)判断key对应的元素是否存在.

    delete(n, "a")                        // 使用内置函数delete删除key为”a“对应的元素.

结构体 Struct

    type S struct {
        A int
        B, c string
    }
    var s S = S{0, "1", "2"}
var s S = S{B: "1", A: 0}
var a S
var b = S{}
fmt.Println(a == b)    // true
package main

type (
    A struct {
        v int
    }

    // 定义结构体B,嵌入结构体A作为匿名字段
    B struct {
        A
    }

    // 定义结构体C,嵌入结构体A的指针作为匿名字段
    C struct {
        *A
    }
)

func (a *A) setV(v int) {
    a.v = v
}

func (a A) getV() int {
    return a.v
}

func (b B) getV() string {
    return "B"
}

func (c *C) getV() bool {
    return true
}

func main() {
    a := A{}
    b := B{}    // 初始化结构体B,其内匿名字段A默认零值是A{}
    c := C{&A{}}    // 初始化结构体C,其内匿名指针字段*A默认零值是nil,需要初始化赋值

    println(a.v)

    // 结构体A嵌入B,A内字段自动提升到B
    println(b.v)

    // 结构体指针*A嵌入C,*A对应结构体内字段自动提升到C
    println(c.v)

    a.setV(3)
    b.setV(5)
    c.setV(7)
    println(a.getV(), b.A.getV(), c.A.getV())
    println(a.getV(), b.getV(), c.getV())
}
    package main

    import "fmt"

    type Integer int

    // 声明变量a为空的匿名结构体类型
    var a struct{}

    // 声明变量b为包含一个字段的匿名结构体类型
    var b struct{ x int }

    // 声明变量c为包含两个字段的匿名结构体类型
    var c struct {
        u int
        v bool
    }

    func main() {
        printa(a)
        b.x = 1
        fmt.Printf("bx: %#v\n", printb(b))    // bx: struct { y uint8 }{y:0x19}
        printc(c)

        // 声明d为包含3个字段的匿名结构体并初始化部分字段
        d := struct {
            x int
            y complex64
            z string
        }{
            z: "asdf",
            x: 111,
        }
        d.y = 22 + 333i
        fmt.Printf("d: %#v\n", d)    // d: struct { x int; y complex64; z string }{x:111, y:(22+333i), z:"asdf"}

        // 声明变量e为包含两个字段的匿名结构体类型
        // 包含1个匿名结构体类型的命名字段和1个命名类型的匿名字段
        e := struct {
            a struct{ x int }
            // 结构体组合嵌入匿名字段只支持命名类型
            Integer
        }{}
        e.Integer = 444
        fmt.Printf("e: %#v\n", e)    // e: struct { a struct { x int }; main.Integer }{a:struct { x int }{x:0}, Integer:444}
    }

    // 函数参数为匿名结构体类型时,传入参数类型声明必须保持一致
    func printa(s struct{}) {
        fmt.Printf("a: %#v\n", s)    // a: struct {}{}
    }

    // 函数入参和返回值都支持匿名结构体类型
    func printb(s struct{ x int }) (x struct{ y byte }) {
        fmt.Printf("b: %#v\n", s)    // b: struct { x int }{x:1}
        x.y = 25
        return
    }

    func printc(s struct {u int; v bool }) {
        fmt.Printf("c: %#v\n", s)    // c: struct { u int; v bool }{u:0, v:false}
    }

指针 Pointer

package main

import "fmt"

func main() {
	var a int = 1
	var b *int = &a
	var c **int = &b
	var x int = *b
	fmt.Println(a == *b)
	fmt.Println("a = ", a)
	fmt.Println("&a = ", &a)
	fmt.Println("*&a = ", *&a)
	fmt.Println("b = ", b)
	fmt.Println("&b = ", &b)
	fmt.Println("*&b = ", *&b)
	fmt.Println("*b = ", *b)
	fmt.Println("c = ", c)
	fmt.Println("*c = ", *c)
	fmt.Println("&c = ", &c)
	fmt.Println("*&c = ", *&c)
	fmt.Println("**c = ", **c)
	fmt.Println("***&*&*&*&c = ", ***&*&*&*&*&c)
	fmt.Println("x = ", x)
}

>>> true
>>> a =  1
>>> &a =  0xc42000e248
>>> *&a =  1
>>> b =  0xc42000e248
>>> &b =  0xc42000c028
>>> *&b =  0xc42000e248
>>> *b =  1
>>> c =  0xc42000c028
>>> *c =  0xc42000e248
>>> &c =  0xc42000c030
>>> *&c =  0xc42000c028
>>> **c =  1
>>> ***&*&*&*&c =  1
>>> x =  1
    var i int = 1
    pi := &i    // 指向数值的指针

    a := []int{0, 1, 2}
    pa := &a    // 指向引用对象的指针

    var s *S = &S{0, "1", "2"}    // 指向值对象的指针
    var i = new(int)
    var s *S = new(S)
    func main() {
        i := new(int)
        *i = 3
        println(i, *i)    // 0xc208031f80    3

        i = new(int)
        println(i, *i)    // 0xc208031f78    0
    }
    fmt.Println(s.A)
    fmt.Println((*s).A)
    func main() {
        var i int
        var p *int
        var pp **int
        var ppp ***int
        var pppp ****int
        println(i, p, pp, ppp, pppp)    // 0 0x0 0x0 0x0 0x0

        i, p, pp, ppp, pppp = 123, &i, &p, &pp, &ppp
        println(i, p, pp, ppp, pppp)    // 123 0xc208031f68 0xc208031f88 0xc208031f80 0xc208031f78
        println(i, *p, **pp, ***ppp, ****pppp)    // 123 123 123 123 123
    }
    package a

    type X struct {
        A Y
    }
    type Y struct {
        B Z
    }
    type Z struct {
        C int
    }
    package main
    import (
        "a"
        "fmt"
    )

    func main() {
        var x = a.X{}
        var p = &x
        fmt.Println("x: ", x)    // x:  }
        println("p: ", p)    // p:  0xc208055f20
        fmt.Println("*p: ", *p)    // *p:  }
        println("x.A.B.C: ", x.A.B.C)    // x.A.B.C:  0
        //  println("*p.A.B.C: ", *p.A.B.C)    // invalid indirect of p.A.B.C (type int)
        println("(*p).A.B.C: ", (*p).A.B.C)    // (*p).A.B.C:  0
    }

通道 Channel

    var c0 chan int      // 可用来发送和接收int类型的值
    var c1 chan<- int    // 可用来发送int类型的值
    var c2 <-chan int    // 可用来接收int类型的值
    c0 := make(chan int)        // 不带缓冲的int类型channel
    c1 := make(chan *int, 10)    // 带缓冲的*int类型指针channel

无缓冲的channe中有值时发送方会阻塞,直到接收方从channel中取出值。
带缓冲的channel在缓冲区已满时发送方会阻塞,直到接收方从channel中取出值。
接收方在channel中无值会一直阻塞。

    c0 <- 3

通过channel接收一个值时,<-作为一元操作符使用。

    i := <-c1
    ch := make(chan string, 1)

    // 发送方,发送值后关闭channel
    ch <- "hello"
    close(ch)


    // 接收方,取出发送的值
    fmt.Println(<-ch)    // 输出: “hello”

    // 再次从已关闭的channel中取值,返回channel传递类型的零值
    fmt.Println(<-ch)    // 输出: 零值,空字符串“”

    // 接收方判断接收到的零值是由发送方发送的还是关闭channel返回的默认值
    s, ok := <-ch
    if ok {
        fmt.Println("Receive value from sender:", s)
    } else {
        fmt.Println("Get zero value from closed channel")
    }

    // 向已关闭的通道发送值会产生运行时恐慌panic
    ch <- "hi"
    // 再次关闭已经关闭的通道也会产生运行时恐慌panic
    close(ch)
    package main

    import "fmt"

    func main() {
        // 无缓冲和有缓冲的channel的range用法相同
        var ch = make(chan int)    // make(chan int, 2) 或 make(chan int , 100)
        go func() {
            for i := 0; i < 5; i++ {
                ch <- i
            }
            close(ch)
        }()

        // channel中无发送值且未关闭时会阻塞
        for x := range ch {
            fmt.Println(x)
        }
    }

下面方式与for range用法效果相同

    loop:
        for {
            select {
            case x, ok := <-c:
                if !ok {
                    break loop
                }
                fmt.Println(x)
            }
        }

接口 Interface

    type Abser interface {
        Abs() float64
    }
    type MyFloat float64

    func (f MyFloat) Abs() float64 {
        if f < 0 {
            return float64(-f)
        }
        return float64(f)
    }
    type Reader interface {
        Read(b []byte) (n int)
    }

    type Writer interface {
        Write(b []byte) (n int)
    }

    // 接口ReadWriter组合了Reader和Writer两个接口
    type ReadWriter interface {
        Reader
        Writer
    }

    type File struct {
        // ...
    }

    func (f *File) Read(b []byte) (n int) {
        println("Read", len(b),"bytes data.")
        return len(b)
    }

    func (f *File) Write(b []byte) (n int) {
        println("Write", len(b),"bytes data.")
        return len(b)
    }

    func main() {
        // *File 实现了Read方法和Write方法,所以实现了Reader接口和Writer接口以及组合接口ReadWriter
        var f *File = &File{}
        var r Reader = f
        var w Writer = f
        var rw ReadWriter = f
        bs := []byte("asdf")
        r.Read(bs)
        rw.Read(bs)
        w.Write(bs)
        rw.Write(bs)
    }
    type error interface {
        Error() string
    }

自定义类型

    type (
        A int
        B int8
        C int16
        D rune
        E int32
        F int64
        G uint
        H byte
        I uint16
        J uint32
        K uint64
        L float32
        M float64
        N complex64
        O complex128
        P uintptr
        Q bool
        R string
        S [3]uint8
        T []complex128
        U map[string]uintptr
        V func(i int) (b bool)
        W struct {a, b int}
        X chan int
        Y interface {}
        Z A
    )
    type (
        A *int
        B *int8
        C *int16
        D *rune
        E *int32
        F *int64
        G *uint
        H *byte
        I *uint16
        J *uint32
        K *uint64
        L *float32
        M *float64
        N *complex64
        O *complex128
        P *uintptr
        Q *bool
        R *string
        S *[3]uint8
        T *[]complex128
        U *map[string]uintptr
        V *func(i int) (b bool)
        W *struct {a, b int}
        X *chan int
        Y *interface {}
        Z *A
    )

语句 Statement

分号/括号 ; {

条件语句 if

    if (i < 0)        // 编译错误.
        println(i)

    if i < 0          // 编译错误.
        println(i)

    if (i < 0) {      // 编译通过.
        println(i)
    }

    if (i < 0 || i > 10) {
        println(i)
    }

    if i < 0 {
        println(i)
    } else if i > 5 && i <= 10 {
        println(i)
    } else {
        println(i)
    }
    if (i := 0; i < 1) {    // 编译错误.
        println(i)
    }

    if i := 0; (i < 1) {    // 编译通过.
        println(i)
    }

    if i := 0; i < 0 {      // 使用gofmt格式化代码会自动移除代码中不必要的小括号( )
        println(i)
    } else if i == 0 {
        println(i)
    } else {
        println(i)
    }
    a, b := 0, 1
    if a, b := 3, 4; a > 1 && b > 2 {
        println(a, b)    // 3 4
    }
    println(a, b)        // 0 1
    package main

    func f0() int {return 333}

    func main() {
        x := 9
        checkType(x)
        checkType(f0)
    }

    func checkType(x interface{}) {
        // 断言传入的x为int类型,并获取值
        if i, ok := x.(int); ok {
            println("int: ", i)    // int:  0
        }

        if f, ok := x.(func() int); ok {
            println("func: ", f())    // func:  333
        }

        // 如果传入x类型为int,则可以直接获取其值
        a := x.(int)
        println(a)

        // 如果传入x类型不是byte,则会产生恐慌panic
        b := x.(byte)
        println(b)
    }

分支选择 switch

    switch x {
    case 0:
        println("single const")
    case 1, 2, 3:
        println("const list")
    default:
        println("default")
    }
    switch x *= 2; x {
    case 4: {
        println("single const")
    }
    case 5, 6, 7: {
        println("const list")
    }
    default: {
        println("default")
    }
    }
    switch x /= 3; {
    case x == 8:
        println("expression")
    case x >= 9:
        println("expression")
    default:
        println("default")
    }
    switch {
    case x == 10:
        println("expression")
    case x >= 11:
        println("expression")
    default:
        println("default")
    }
    package main

    import (
        "fmt"
        "code.google.com/p/go.crypto/openpgp/errors"
    )

    func main() {
        var (
            a = 0.1
            b = 2+3i
            c = "asdf"
            d = [...]byte{1, 2, 3}
            e = []complex128{1+2i}
            f = map[string]uintptr{"a": 0}
            g = func(int) bool {return true}
            h = struct { a, b int }{}
            i = &struct {}{}
            j chan int
            k chan <- bool
            l <-chan string
            m errors.SignatureError
        )
        values := []interface{}{nil, a, b, &c, d, e, f, g, &g, h, &h, i, j, k, l, m}
        for _, v := range values {
            typeswitch(v)
        }
    }

    func typeswitch(x interface{}) {
        // switch x.(type) {    // 不使用类型值时
        switch i := x.(type) {
        case nil:
            fmt.Println("x is nil")
        case int, int8, int16, rune, int64, uint, byte, uint16, uint32, uint64, float32, float64, complex64, complex128, uintptr, bool, string:
            fmt.Printf("basic type : %T\n", i)
        case *int, *int8, *int16, *rune, *int64, *uint, *byte, *uint16, *uint32, *uint64, *float32, *float64, *complex64, *complex128, *uintptr, *bool, *string:
            fmt.Printf("basic pointer type : %T\n", i)
        case [3]byte, []complex128, map[string]uintptr:
            fmt.Printf("collection type : %T\n", i)
        case func(i int) (b bool), *func():
            fmt.Printf("function type : %T\n", i)
        case struct {a, b int}, *struct {}:
            fmt.Printf("struct type : %T\n", i)
        case chan int, chan <- bool, <-chan string:
            fmt.Printf("channel type : %T\n", i)
        case error, interface{a(); b()}:
            fmt.Printf("interface type : %T\n", i)
        default:
            fmt.Printf("other type : %T\n", i)
        }
    }

    // output:

    // x is nil
    // basic type : float64
    // basic type : complex128
    // basic pointer type : *string
    // collection type : [3]uint8
    // collection type : []complex128
    // collection type : map[string]uintptr
    // function type : func(int) bool
    // other type : *func(int) bool
    // struct type : struct { a int; b int }
    // other type : *struct { a int; b int }
    // struct type : *struct {}
    // channel type : chan int
    // channel type : chan<- bool
    // channel type : <-chan string
    // interface type : errors.SignatureError
    switch {
    case false:
        println("case 1")
        fallthrough
    case true:
        println("case 2")
        fallthrough
    case false:
        println("case 3")
        fallthrough
    case true:
        println("case 4")
    case false:
        println("case 5")
        fallthrough
    default:
        println("default case")
    }
    // 输出:case 2 case 3 case 4

循环语句 for

    for i := 0; i < 10; i++ {...}
    for i := 0; i < 10; {...}      // 省略迭代语句
    for i := 0; ; i++; {...}       // 省略条件语句
    for ; i < 10; i++ {...}        // 省略初始化语句
    for i := 0; ; {...}            // 省略条件和迭代语句, 分号不能省略
    for ; i < 10; {...}            // 省略初始化和迭代语句, 分号可省略
    for ; ; i++ {...}              // 省略初始化和条件语句, 分号不能省略
    for i < 10 {...}
    for ; ; {...}                  // 分号可省略
    for {...}
    for (i := 0; i < 10; i++) {...}     // 编译错误.
    for i := 0; (i < 10); i++ {...}     // 编译通过.
    for (i < 10) {...}                  // 编译通过.
    a := [5]int{2, 3, 4, 5, 6}


    for k, v := range a {
        fmt.Println(k, v)    // 输出:0 2, 1 3, 2 4, 3 5, 4 6
    }

    for k := range a {
        fmt.Println(k)    // 输出:0 1 2 3 4
    }

    for _ = range a {
        fmt.Println("print without care about the key and value")
    }

    for range a {
        fmt.Println("new syntax – print without care about the key and value")
    }
    for k, v := range s {
        if v == 3 {
            continue    // 结束本次循环,进入下一次循环中
        } else if v == 5 {
            break    // 结束整个for循环
        } else {
            goto SOMEWHERE    // 跳转到标签指定的代码处
        }
    }
    package main
    import "fmt"

    func main() {
        var arr = [...]int{33, 22, 11, 0}
        // 遍历数组,取一位值时为索引值
        for k := range arr {
            fmt.Printf("%d, ", k)   // 0, 1, 2, 3,
        }
        fmt.Println()
        // 遍历数组,取两位值时,第一位为索引值,第二位为元素值
        for k, v := range arr {
            fmt.Printf("%d %d, ", k, v) // 0 33, 1 22, 2 11, 3 0,
        }
        fmt.Println()

        // 遍历数组指针,取一位值时为索引值
        for k := range &arr {
            fmt.Printf("%d, ", k)   // 0, 1, 2, 3,
        }
        fmt.Println()
        // 遍历数组指针,取两位值时,第一位为索引值,第二位为元素值
        for k, v := range &arr {
            fmt.Printf("%d %d, ", k, v) // 0 33, 1 22, 2 11, 3 0,
        }
        fmt.Println()

        var slc = []byte{44, 55, 66, 77}
        // 遍历切片,取一位值时为索引值
        for k := range slc {
            fmt.Printf("%d, ", k)   // 0, 1, 2, 3,
        }
        fmt.Println()
        // 遍历切片,取两位值时,第一位为索引值,第二位为元素值
        for k, v := range slc {
            fmt.Printf("%d %d, ", k, v) // 0 44, 1 55, 2 66, 3 77,
        }
        fmt.Println()

        var str = "abc一二3"
        // 遍历字符串,取一位值时为字节索引值
        for k := range str {
            fmt.Printf("%d, ", k)   // 0, 1, 2, 3, 6, 9,
        }
        fmt.Println()
        // 遍历字符串,取两位值时,第一位为字节索引值,第二位为Unicode字符
        for k, v := range str {
            fmt.Printf("%d %d %s, ", k, v, string(v))   // 0 97 a, 1 98 b, 2 99 c, 3 19968 一, 6 20108 二, 9 51 3,
        }
        fmt.Println()

        var mp = map[int]string{5:"A", 9:"B"}
        // 遍历map,取一位值时为键key
        for k := range mp {
            fmt.Printf("%d, ", k)   // 9, 5,
        }
        fmt.Println()
        // 遍历map,取两位值时,第一位为键key,第二位为元素值value
        for k, v := range mp {
            fmt.Printf("%d %s, ", k, v) // 5 A, 9 B,
        }
        fmt.Println()

        var ch = make(chan int)
        go func() {
            for i := 0; i < 5; i++ {
                ch <- i
            }
            close(ch)
        }()
        // 遍历channel时,只能取一位值,为发送方发送到channel中的值
        for x := range ch {
            fmt.Printf("%d ", x)    // 0 1 2 3 4
        }
    }

通道选择 select

    ch1, ch2 := make(chan int), make(chan int)

    // 因为没有值发送到select中的任一case的channel中,此select将会阻塞
    select {
    case <-ch1:
        println("channel 1")
    case <-ch2:
        println("channel 2")
    }
    ch1, ch2 := make(chan int), make(chan int)

    // 因为没有值发送到select中的任一case的channel中,此select将会执行default分支
    select {
    case <-ch1:
        println("channel 1")
    case <-ch2:
        println("channel 2")
    default:
        println("default")
    }
    func main() {
        for {
            select {
            case <-time.Tick(time.Second):
                println("Tick")
            case <-time.After(5 * time.Second):
                println("Finish")
            default:
                println("default")
                time.Sleep(5e8)
            }
        }
    }

延迟执行 defer

    package main

    func main() {
        defer print(0)
        defer print(1)
        defer print(2)
        defer print(3)
        defer print(4)

        for i := 5; i <= 9; i++ {
            defer print(i)
        }
        // 输出:9876543210
    }
    package main

    func main() {
        println(f())    // 返回: 15
    }

    func f() (i int) {
        defer func() {
            i *= 5
        }()
        return 3
    }
    ch <- "hello"
    defer close(ch)

关闭IO流

    f, err := os.Open("file.xxx")
    defer f.Close()

关闭数据库连接

    db, err := sql.Open("mysql","user:password@tcp(127.0.0.1:3306)/hello")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
    func main() {
        f()
        fmt.Println("main normal...")
    }

    func f() {
        defer func() {
            if r := recover(); r != nil {
                fmt.Println("catch:", r)
            }
        }()
        p()
        fmt.Println("normal...")
    }

    func p() {
        panic("exception...")
    }

跳转语句 goto

    package main

    func main() {
        i := 0
    loop:
        i++
        if i < 5 {
            goto loop
        }
        println(i)
    }
    func main() {
        i, sum := 0, 0
    head:
        for ; i <= 10; i++ {
            if i < 5 {
                i++    // 此处必须单独调用一次,因为goto跳转时不会执行for循环的自增语句
                goto head    // continue
            }
            if i > 9 {
                goto tail    // break
            }
            sum += i
        }
    tail:
        println(sum)    // 输出:35
    }

函数 Function

函数声明 Declare

    func f0() {/*...*/}

    func f1(a int) {/*...*/}

    func f2(a int, b byte) {/*...*/}
    func f3(a ...int) {/*...*/}

    func f4(a int, b bool, c ...string) {/*...*/}
    func f0() {
        return
    }

    func f1() int {
        return 0
    }

    func f2() (int, string) {
        return 0, "A"
    }
    func f() (a int, b string) {
        a = 1
        b = "B"
        return    // 即使return后面没有跟变量,关键字在函数结尾也是必须的
        // 或者 return a, b
    }
    func f0(a,b,c int) {/*...*/}

    func f1() (a,b,c int) {/*...*/}

    func f2(a,b int, c,d byte) (x,y int, z,s bool) {/*...*/}

函数闭包 Closure

    package main

    type Myfunc func(i int) int

    func f0(name string){
        println(name)
    }

    func main() {
        var a = f0
        a("hello")    // hello

        var f1 Myfunc = func(i int) int {
            return i
        }
        fmt.Println(f1(3))    // 3

        var f2 func() int = func() int {
            return 0
        }
        fmt.Println(f2())     // 0

        // 省略部分关键字
        var f3 func() = func() {/*...*/}
        var f4 = func() {/*...*/}
        f5 := func() {/*...*/}
    }

内建函数 Builtin

    func append(slice []Type, elems ...Type) []Type
> 内建函数append将元素追加到切片的末尾。若它有足够的容量,其目标就会重新切片以容纳新的元素。否则,就会分配一个新的基本数组。append返回更新后的切片,因此必须存储追加后的结果。
    slice = append(slice, elem1, elem2)
    slice = append(slice, anotherSlice...)
> 作为特例,可以向一个字节切片append字符串,如下:
    slice = append([]byte("hello "), "world"...)


    func cap(v Type) int
> 内建函数cap返回 v 的容量,这取决于具体类型:  
数组:v中元素的数量,与 len(v) 相同  
数组指针:*v中元素的数量,与len(v) 相同  
切片:切片的容量(底层数组的长度);若 v为nil,cap(v) 即为零  
信道:按照元素的单元,相应信道缓存的容量;若v为nil,cap(v)即为零  


    func close(c chan<- Type)
> 内建函数close关闭信道,该通道必须为双向的或只发送的。它应当只由发送者执行,而不应由接收者执行,其效果是在最后发送的值被接收后停止该通道。在最后的值从已关闭的信道中被接收后,任何对其的接收操作都会无阻塞的成功。对于已关闭的信道,语句:
    x, ok := <-c    // ok值为false


    func complex(r, i FloatType) ComplexType
> 使用实部r和虚部i生成一个复数。
    c := complex(1, 2)
    fmt.Println(c)    // (1+2i)


    func copy(dst, src []Type) int
> 内建函数copy将元素从来源切片复制到目标切片中,也能将字节从字符串复制到字节切片中。copy返回被复制的元素数量,它会是 len(src) 和 len(dst) 中较小的那个。来源和目标的底层内存可以重叠。
    a, b, c := []byte{1, 2, 3}, make([]byte, 2), 0
    fmt.Println("a:", a, " b:", b, " c: ", c)    // a: [1 2 3]  b: [0 0]  c:  0

    c = copy(b, a)
    fmt.Println("a:", a, " b:", b, " c: ", c)    // a: [1 2 3]  b: [1 2]  c:  2

    b = make([]byte, 5)
    c = copy(b, a)
    fmt.Println("a:", a, " b:", b, " c: ", c)    // a: [1 2 3]  b: [1 2 3 0 0]  c:  3

    s := "ABCD"
    c = copy(b, s)
    fmt.Println("s:", s, " b:", b, " c: ", c)    // s: ABCD  b: [65 66 67 68 0]  c:  4


    func delete(m map[Type]Type1, key Type)
> 内建函数delete按照指定的键将元素从映射中删除。若m为nil或无此元素,delete不进行操作。
    m := map[int]string{
        0: "A",
        1: "B",
        2: "C",
    }
    delete(m, 1)
    fmt.Println(m)    // map[2:C 0:A]

    delete(m, 3)    // 此行代码执行没有任何操作,也不会报错。


    func imag(c ComplexType) FloatType
> 返回复数c的虚部。
    c := 2+5i
    fmt.Println(imag(c))    // 5


    func len(v Type) int
> 内建函数len返回 v 的长度,这取决于具体类型:  
数组:v中元素的数量  
数组指针:*v中元素的数量(v为nil时panic)  
切片、映射:v中元素的数量;若v为nil,len(v)即为零  
字符串:v中字节的数量,计算字符数量使用`utf8.RuneCountInString()`  
通道:通道缓存中队列(未读取)元素的数量;若v为 nil,len(v)即为零


    func make(Type, size IntegerType) Type
> 内建函数make分配并初始化一个类型为切片、映射、或通道的对象。其第一个实参为类型,而非值。make的返回类型与其参数相同,而非指向它的指针。其具体结果取决于具体的类型:  
切片:size指定了其长度。该切片的容量等于其长度。切片支持第二个整数实参可用来指定不同的容量;它必须不小于其长度,因此 make([]int, 0, 10) 会分配一个长度为0,容量为10的切片。  
映射:初始分配的创建取决于size,但产生的映射长度为0。size可以省略,这种情况下就会分配一个小的起始大小。  
通道:通道的缓存根据指定的缓存容量初始化。若 size为零或被省略,该信道即为无缓存的。


    func new(Type) *Type
> 内建函数new分配内存。其第一个实参为类型,而非值。其返回值为指向该类型的新分配的零值的指针。


    func panic(v interface{})
> 内建函数panic停止当前Go程的正常执行。当函数F调用panic时,F的正常执行就会立刻停止。F中defer的所有函数先入后出执行后,F返回给其调用者G。G如同F一样行动,层层返回,直到该Go程中所有函数都按相反的顺序停止执行。之后,程序被终止,而错误情况会被报告,包括引发该恐慌的实参值,此终止序列称为恐慌过程。


    func print(args ...Type)
> 内建函数print以特有的方法格式化参数并将结果写入标准错误,用于自举和调试。


    func println(args ...Type)
> println类似print,但会在参数输出之间添加空格,输出结束后换行。


    func real(c ComplexType) FloatType
> 返回复数c的实部。
    c := 2+5i
    fmt.Println(real(c))    // 2


    func recover() interface{}
> 内建函数recover允许程序管理恐慌过程中的Go程。在defer的函数中,执行recover调用会取回传至panic调用的错误值,恢复正常执行,停止恐慌过程。若recover在defer的函数之外被调用,它将不会停止恐慌过程序列。在此情况下,或当该Go程不在恐慌过程中时,或提供给panic的实参为nil时,recover就会返回nil。

初始化函数 init

    func init() {
        // ...
    }
    package main

    func main() {
    }

    func init() {
        println("init1...")
    }
    func init() {
        println("init2...")
    }
    func init() {
        println("init3...")
    }
    func init() {
        println("init...")
    }

    func main() {
        init()    // undefined: init
    }

方法 Method

    package main

    type A struct {
        x, y int
    }

    // 定义结构体的方法,'_'表示方法内忽略使用结构体、字段及其他方法
    func (_ A) echo_A() {
        println("(_ A)")
    }

    // 同上
    func (A) echoA(s string) {
        println("(A)", s)
    }

    // 定义结构体指针的方法,'_'表示方法内忽略使用结构体指针、字段及其他方法
    func (_ *A) echo_жA() {
        println("(_ *A)")
    }

    // 同上
    func (*A) echoжA(s string) {
        println("(*A)", s)
    }

    // 定义结构体的方法,方法内可以引用结构体、字段及其他方法
    func (a A) setX(x int) {
        a.x = x
    }

    // 定义结构体指针的方法,方法内可以引用结构体、结构体指针、字段及其他方法
    func (a *A) setY(y int) {
        a.y = y
    }

    func main() {
        var a A    // a = A{}
        a.setX(3)
        a.setY(6)
        println(a.x, a.y) // 0  6
        a.echo_A()    // (_ A)
        a.echoA("a")    // (A) a
        a.echo_жA()    // (_ *A)
        a.echoжA("a")    // (*A) a

        // 以下是定义在结构体值上的方法原型,通过调用结构体类型上定义的函数,传入结构体的值
        A.echo_A(a)    // (_ A)
        A.echoA(a, "a")    // (A) a
        // A.echo_жA(a)    // A.echo_жA未定义
        // A.echoжA(a)    //  A.echoжA未定义
        A.setX(a, 4)
        // A.setY(a, 7)    // A.setY未定义
        println(a.x) // 0


        b := &a
        b.setX(2)
        b.setY(5)
        println(b.x, b.y) // 0  5
        b.echo_A()    // (_ A)
        b.echoA("b")    // (A) b
        b.echo_жA()    // (_ *A)
        b.echoжA("b")    // (*A) b

        // 以下是定义在结构体指针上的方法原型,通过调用结构体类型指针上定义的函数,传入结构体的指针
        (*A).echo_A(b)    // (_ A)
        (*A).echoA(b, "b")    // (A) b
        (*A).echo_жA(b)    // (_ *A)
        (*A).echoжA(b, "b")    // (*A) b
        (*A).setX(b, 1)
        (*A).setY(b, 8)
        println(b.x, b.y)    // 0   8

        // 调用结构体空指针上的方法,以下注释掉的代码都是空指针错误
        var c *A    // c = nil
        // c.setX(2)
        // c.setY(5)
        // println(c.x, c.y)
        // c.echo_A()
        // c.echoA()
        c.echo_жA()    // (_ *A)
        c.echoжA("c")    // (*A) c

        // (*A).echo_A(c)
        // (*A).echoA(c)
        (*A).echo_жA(c)    // (_ *A)
        (*A).echoжA(c, "c")    // (*A) c
        // (*A).setX(c, 1)
        // (*A).setY(c, 8)
        // println(c.x, c.y)
    }

并发 Concurrency

    package main

    import (
        "time"
    )

    func say(i int) {
        println("goroutine:", i)
    }

    func main() {
        for i := 1; i <= 5; i++ {
            go say(i)
        }
        say(0)
        time.Sleep(5 * time.Second)
    }
主协程goroutine输出0,其他由go启动的几个子协程分别输出1~5

> goroutine: 0  
goroutine: 1  
goroutine: 2  
goroutine: 3  
goroutine: 4  
goroutine: 5
    package main

    import (
        "sync"
        "time"
    )

    var mu sync.Mutex
    var i int

    func main() {
        for range [5]byte{} {
            go Add()
        }
        time.Sleep(5*time.Second)
        println(i)
    }

    func Add() {
        // 使用互斥锁防止多个协程goroutine同时修改共享变量
        // 只能限制同时访问此方法修改变量,在方法外修改则限制是无效的
        mu.Lock()
        defer mu.Unlock()
        i++
    }

使用通道channel进行同步

    package main

    import (
        "time"
    )

    var i int
    var ch = make(chan byte, 1)

    func main() {
        for range [5]byte{} {
            go Add()
        }
        time.Sleep(5*time.Second)
        println(i)
    }

    func Add() {
        ch <- 0
        i++
        <-ch
    }
    // 上一个例子只是将channel用作同步开关,稍做修改即可在不同goroutine间通信
    package main

    import (
        "time"
    )

    var i int
    var ch = make(chan int, 1)

    func main() {
        for range [5]byte{} {
            go Add()
        }
        ch <- i
        time.Sleep(5*time.Second)
        i = <-ch
        println(i)
    }

    func Add() {
        // 从channel中接收的值是来自其他goroutine发送的
        x := <-ch
        x++
        ch <- x
    }

测试 Testing

单元测试 Unit

    package testgo

    import "math"

    func Sum(min, max int) (sum int) {
        if min < 0 || max < 0 || max > math.MaxInt32 || min > max {
            return 0
        }

        for ; min <= max; min++ {
            sum += min
        }
        return
    }
    package testgo

    import "testing"

    func TestSum(t *testing.T) {
        s := Sum(1, 0)
        t.Log("Sum 1 to 0:", s)
        if 0 != s {
            t.Error("not equal.")
        }
        s = Sum(1, 10)
        t.Log("Sum 1 to 10:", s)
        if 55 != s {
            t.Error("not equal.")
        }
    }

在当前包中执行测试:go test -v

=== RUN TestSum
— PASS: TestSum (0.00s)
t0_test.go:7: Sum 1 to 0: 0
t0_test.go:12: Sum 1 to 10: 55
PASS
ok /home/cxy/go/src/testgo 0.004s

基准测试 Benchmark

    package testgo

    import "testing"

    func BenchmarkSum(b *testing.B) {
        b.Logf("Sum 1 to %d: %d\n", b.N, Sum(1, b.N))
    }

在当前包中执行测试:go test -v -bench .

BenchmarkSum 2000000000 0.91 ns/op
— BENCH: BenchmarkSum
t0_test.go:19: Sum 1 to 1: 1
t0_test.go:19: Sum 1 to 100: 5050
t0_test.go:19: Sum 1 to 10000: 50005000
t0_test.go:19: Sum 1 to 1000000: 500000500000
t0_test.go:19: Sum 1 to 100000000: 5000000050000000
t0_test.go:19: Sum 1 to 2000000000: 2000000001000000000
ok /home/cxy/go/src/testgo 1.922s

-->