• 跟着实例学Go语言(一)


    本教程全面涵盖了Go语言基础的各个方面。一共80个例子,每个例子对应一个语言特性点,既适合新人快速上手,也适合工作中遇到问题速查知识点。
    教程代码示例来自go by example,文字部分来自本人自己的理解。

    本文是教程系列的第一部分,共计20个例子、约1万字。

    系列文章快速跳转:
    跟着实例学Go语言(一)
    跟着实例学Go语言(二)
    跟着实例学Go语言(三)
    跟着实例学Go语言(四)

    1. Hello World

    下面的例子演示了如何打印经典的“Hello world”语句,以及运行和编译go代码的方法。

    package main
    import "fmt"
    
    func main() {
    	// 打印语句并换行
        fmt.Println("hello world")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    // 可以通过go run命令直接从代码运行
    $ go run hello-world.go
    hello world
    
    // 也可以先通过go build先编译成可执行文件
    $ go build hello-world.go
    $ ls
    hello-world    hello-world.go
    
    // 再运行可执行文件
    $ ./hello-world
    hello world
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2. Values

    下面的代码演示了打印不同类型变量的方法。

    package main
    
    import "fmt"
    
    func main() {
    	// 支持字符串通过加号拼接 
        fmt.Println("go" + "lang")
        
    	// Println支持多个不同类型参数,参数之间会用空格拼接起来输出
        fmt.Println("1+1 =", 1+1)
        fmt.Println("7.0/3.0 =", 7.0/3.0)
        
    	// 支持输出bool类型变量
        fmt.Println(true && false)
        fmt.Println(true || false)
        fmt.Println(!true)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    $ go run values.go
    golang
    1+1 = 2
    7.0/3.0 = 2.3333333333333335
    false
    true
    false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3. Variables

    下面的例子演示了定义和初始化变量的方法。Go的编译器可以做类型推导,无需显示指定类型。若对变量不做显式初始化,则会自动赋值为零值

    package main
    
    import "fmt"
    
    func main() {
    
    	// 定义并初始化一个变量(类型会被自动推导为string)
        var a = "initial"
        fmt.Println(a)
        
    	// 可以一次定义多个变量
        var b, c int = 1, 2
        fmt.Println(b, c)
        
      // 定义并初始化一个bool变量
        var d = true
        fmt.Println(d)
    
    	// 定义变量并指定类型,但不显式初始化(自动初始化为零值,0)
        var e int
        fmt.Println(e)
        
    	// 更简洁的变量定义语法,推荐
        f := "apple"
        fmt.Println(f)
    }
    
    • 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
    $ go run variables.go
    initial
    1 2
    true
    0
    apple
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    附:Go中的类型分类及零值大全
    Go中的类型分类及零值大全
    Go中的变量类型分为值类型、指针类型和引用类型三类。它们在函数传参时的表现有差异:值类型传递时是拷贝复制,而指针类型实际存储的是所指向变量的地址,经过拷贝复制后,在函数内部对所指变量的任何修改都能生效,外部变量会实际改变。至于引用类型,它并非是Go中的原生概念,而是一个语法糖,实际在内部封装了一个指针,例如map类型本质上就是 *hmap;这样做的好处是无需再显式使用指针,使用更方便。

    4. Constants

    下面的例子演示了用const关键字定义的常量。

    package main
    
    import (
        "fmt"
        "math"
    )
    
    // 定义常量时用const代替var
    const s string = "constant"
    
    func main() {
        fmt.Println(s)
    	// 定义常量时也可不指定类型,此时无类型
        const n = 500000000
    
    	// numeric常量支持任意精度的运算
        const d = 3e20 / n
        fmt.Println(d)
        
    	// 可以通过强制转型赋予常量类型
        fmt.Println(int64(d))
    
    	// 通过传参也可以为常量指定类型,如Sin函数指定float64
        fmt.Println(math.Sin(n))
    
    	// 不能将非const变量的运算结果赋值给const变量,否则会编译报错!
    	// i := 100
    	// const j = 100
    	// const k = i + j
    
    }
    
    • 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
    $ go run constant.go 
    constant
    6e+11
    600000000000
    -0.28470407323754404
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5. For

    下面例子展示了用for关键字实现的循环。Go中没有其他语言通常使用的while关键字,所有循环都通过for来实现。

    package main
    
    import "fmt"
    
    func main() {
    
        i := 1
        
     	// 类似于其他语言中的while
        for i <= 3 {
            fmt.Println(i)
            i = i + 1
        }
    
    	// 常规for循环
        for j := 7; j <= 9; j++ {
            fmt.Println(j)
        }
    
    	// 类似于其他语言中的while(true)
        for {
            fmt.Println("loop")
            break
        }
    
        for n := 0; n <= 5; n++ {
            if n%2 == 0 {
                continue
            }
            fmt.Println(n)
        }
    }
    
    • 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
    • 32
    $ go run for.go
    1
    2
    3
    7
    8
    9
    loop
    1
    3
    5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    6. If/Else

    下面例子展示了用if/else关键字实现的条件判断。

    package main
    
    import "fmt"
    
    func main() {
    	// if条件句无需加括号
        if 7%2 == 0 {
            fmt.Println("7 is even")
        // 注意}和else必须在同一行,否则会编译报错
        } else {
            fmt.Println("7 is odd")
        }
    
        if 8%4 == 0 {
            fmt.Println("8 is divisible by 4")
        }
    
    	// if可支持多条语句,用分号分隔
        if num := 9; num < 0 {
            fmt.Println(num, "is negative")
        } else if num < 10 {
            fmt.Println(num, "has 1 digit")
        } else {
            fmt.Println(num, "has multiple digits")
        }
    }
    
    • 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
    $ go run if-else.go 
    7 is odd
    8 is divisible by 4
    9 has 1 digit
    
    • 1
    • 2
    • 3
    • 4

    7. Switch

    下面例子展示了switch关键字实现的分支条件判断。

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
    
        i := 2
        fmt.Print("Write ", i, " as ")
        switch i {
        case 1:
            fmt.Println("one")
        // 注意这里无需加break,一个条件命中后自动退出,否则继续遍历其他条件
        case 2:
            fmt.Println("two")
        case 3:
            fmt.Println("three")
        }
    
        switch time.Now().Weekday() {
        case time.Saturday, time.Sunday:
            fmt.Println("It's the weekend")
        default:
            fmt.Println("It's a weekday")
        }
    
        t := time.Now()
        switch {
        case t.Hour() < 12:
            fmt.Println("It's before noon")
        default:
            fmt.Println("It's after noon")
        }
    
    	// 可以用于类型判断
        whatAmI := func(i interface{}) {
            switch t := i.(type) {
            case bool:
                fmt.Println("I'm a bool")
            case int:
                fmt.Println("I'm an int")
            default:
                fmt.Printf("Don't know type %T\n", t)
            }
        }
        whatAmI(true)
        whatAmI(1)
        whatAmI("hey")
    }
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    $ go run switch.go 
    Write 2 as two
    It's a weekday
    It's after noon
    I'm a bool
    I'm an int
    Don't know type string
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    8. Arrays

    下面例子展示了Go中数组的用法。数组用于存储有序、固定数量的元素。

    package main
    
    import "fmt"
    
    func main() {
    
       // 定义数组变量,需要指明元素类型
       var a [5]int
       // 数组类型作为参数,可以直接打印出所有元素变量
       fmt.Println("emp:", a)
    
       a[4] = 100
       fmt.Println("set:", a)
       fmt.Println("get:", a[4])
    
       // 用len方法获取数组长度
       fmt.Println("len:", len(a))
    
       // 也可以在定义时直接初始化
       b := [5]int{1, 2, 3, 4, 5}
       fmt.Println("dcl:", b)
    
       // 二维数组的定义方法
       var twoD [2][3]int
       for i := 0; i < 2; i++ {
           for j := 0; j < 3; j++ {
               twoD[i][j] = i + j
           }
       }
       fmt.Println("2d: ", twoD)
    }
    
    • 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
    $ go run arrays.go
    emp: [0 0 0 0 0]
    set: [0 0 0 0 100]
    get: 100
    len: 5
    dcl: [1 2 3 4 5]
    2d:  [[0 1 2] [1 2 3]]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    9. Slices

    下面例子介绍了切片的用法。切片是有序、不定长的数据结构,它的用法和数组非常类似,区别在于前者是不定长,后者是定长的。另一个区别是,前者是引用类型,后者是值类型,在函数传参时需要注意区分。

    package main
    
    import "fmt"
    
    func main() {
       // slice需要用make初始化,否则会赋成零值nil
       s := make([]string, 3)
       fmt.Println("emp:", s)
    
       s[0] = "a"
       s[1] = "b"
       s[2] = "c"
       fmt.Println("set:", s)
       fmt.Println("get:", s[2])
    
       fmt.Println("len:", len(s))
       
       // 可以动态往尾部添加元素
       s = append(s, "d")
       s = append(s, "e", "f")
       fmt.Println("apd:", s)
    
       c := make([]string, len(s))
       // copy可用于拷贝slice
       copy(c, s)
       fmt.Println("cpy:", c)
    
       // 可以灵活按下标生成新slice
       l := s[2:5]
       fmt.Println("sl1:", l)
    
       l = s[:5]
       fmt.Println("sl2:", l)
    
       l = s[2:]
       fmt.Println("sl3:", l)
    
       // slice可以和array一样在定义时初始化
       t := []string{"g", "h", "i"}
       fmt.Println("dcl:", t)
    
       twoD := make([][]int, 3)
       for i := 0; i < 3; i++ {
           innerLen := i + 1
           twoD[i] = make([]int, innerLen)
           for j := 0; j < innerLen; j++ {
               twoD[i][j] = i + j
           }
       }
       fmt.Println("2d: ", twoD)
    }
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    $ go run slices.go
    emp: [  ]
    set: [a b c]
    get: c
    len: 3
    apd: [a b c d e f]
    cpy: [a b c d e f]
    sl1: [c d e]
    sl2: [a b c d e]
    sl3: [c d e f]
    dcl: [g h i]
    2d:  [[0] [1 2] [2 3 4]]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    10. Maps

    下面例子展示了map的用法。map用于key-value二元关系数据的存储。

    package main
    
    import "fmt"
    
    func main() {
    	// map是引用类型,也需要用make初始化
        m := make(map[string]int)
    
        m["k1"] = 7
        m["k2"] = 13
    
        fmt.Println("map:", m)
    
        v1 := m["k1"]
        fmt.Println("v1: ", v1)
    
        fmt.Println("len:", len(m))
    
        delete(m, "k2")
        fmt.Println("map:", m)
    	
    	// 可以通过返回的第二个参数判断key是否存在于map中
        _, prs := m["k2"]
        fmt.Println("prs:", prs)
    
    	// 可以在定义时直接初始化
        n := map[string]int{"foo": 1, "bar": 2}
        fmt.Println("map:", n)
    }
    
    • 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
    $ go run maps.go 
    map: map[k1:7 k2:13]
    v1:  7
    len: 2
    map: map[k1:7]
    prs: false
    map: map[bar:2 foo:1]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    11. Range

    下面例子展示了range关键字在遍历slice、map和string时的用法。

    package main
    
    import "fmt"
    
    func main() {
    
        nums := []int{2, 3, 4}
        sum := 0
        for _, num := range nums {
            sum += num
        }
        fmt.Println("sum:", sum)
    
    	// 遍历slice返回的变量,第一个是下标,第二个是对应下标的元素
        for i, num := range nums {
            if num == 3 {
                fmt.Println("index:", i)
            }
        }
    
    	// 遍历map返回的变量,第一个是key,第二个是value
        kvs := map[string]string{"a": "apple", "b": "banana"}
        for k, v := range kvs {
            fmt.Printf("%s -> %s\n", k, v)
        }
    
        for k := range kvs {
            fmt.Println("key:", k)
        }
    
    	// 遍历string返回的变量,第一个是对应rune的byte的起始下标,第二个是对应的rune
        for i, c := range "go" {
            fmt.Println(i, c)
        }
        for i, c := range "我们" {
            fmt.Println(i, c)
        }
    }
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    $ go run range.go
    sum: 9
    index: 1
    a -> apple
    b -> banana
    key: a
    key: b
    0 103
    1 111
    0 25105
    3 20204
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    12. Functions

    下面例子展示了func关键字定义的函数。

    package main
    
    import "fmt"
    
    // 定义函数,顺序:func、函数名、参数列表、返回值类型
    func plus(a int, b int) int {
    
        return a + b
    }
    
    func plusPlus(a, b, c int) int {
        return a + b + c
    }
    
    func main() {
    
        res := plus(1, 2)
        fmt.Println("1+2 =", res)
    
        res = plusPlus(1, 2, 3)
        fmt.Println("1+2+3 =", res)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    $ go run functions.go 
    1+2 = 3
    1+2+3 = 6
    
    • 1
    • 2
    • 3

    13. Multiple Return Values

    下面例子展示了拥有多个返回值的函数。

    package main
    
    import "fmt"
    
    func vals() (int, int) {
        return 3, 7
    }
    
    func main() {
    
        a, b := vals()
        fmt.Println(a)
        fmt.Println(b)
    
    	// 可以用_占位,表示这个变量不实际使用
        _, c := vals()
        fmt.Println(c)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    $ go run multiple-return-values.go
    3
    7
    7
    
    • 1
    • 2
    • 3
    • 4

    14. Variadic Functions

    下面例子展示了变长参数函数的用法。变长参数提供了传参的灵活性,可以是多个不定数量的参数,也可以是slice。

    package main
    
    import "fmt"
    
    // 用...表示变长参数
    func sum(nums ...int) {
        fmt.Print(nums, " ")
        total := 0
    
        for _, num := range nums {
            total += num
        }
        fmt.Println(total)
    }
    
    func main() {
    
    	// 可用于多个参数
        sum(1, 2)
        sum(1, 2, 3)
        
    	// 也可用于slice,注意要加...
        nums := []int{1, 2, 3, 4}
        sum(nums...)
    }
    
    • 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
    $ go run variadic-functions.go 
    [1 2] 3
    [1 2 3] 6
    [1 2 3 4] 10
    
    • 1
    • 2
    • 3
    • 4

    15. Closures

    下面的例子展示了闭包的概念。闭包一般与匿名函数相关,匿名函数可以引用外部函数中定义的变量,对其形成闭包。该变量可作为“半全局变量”,生命周期存在于多次匿名函数调用中,任何对它的修改都可在匿名函数中可见。

    package main
    
    import "fmt"
    
    func intSeq() func() int {
        i := 0
        // 匿名函数对外部定义的i形成闭包
        return func() int {
            i++
            return i
        }
    }
    
    func main() {
    
        nextInt := intSeq()
        
    	// 每次调用匿名函数都会将i加1
        fmt.Println(nextInt())
        fmt.Println(nextInt())
        fmt.Println(nextInt())
    
        newInts := intSeq()
        fmt.Println(newInts())
    }
    
    • 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
    $ go run closures.go
    1
    2
    3
    1
    
    • 1
    • 2
    • 3
    • 4
    • 5

    16. Recursion

    下面例子展示了函数递归的用法。

    package main
    
    import "fmt"
    
    func fact(n int) int {
        if n == 0 {
            return 1
        }
        return n * fact(n-1)
    }
    
    func main() {
        fmt.Println(fact(7))
    
        var fib func(n int) int
    
        fib = func(n int) int {
            if n < 2 {
                return n
            }
    
            return fib(n-1) + fib(n-2)
        }
    
        fmt.Println(fib(7))
    }
    
    • 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
    $ go run recursion.go 
    5040
    13
    
    • 1
    • 2
    • 3

    17. Pointers

    下面例子展示了指针的用法。对于值类型的变量,如果传参给函数后需要在内部做修改,那么需要使用指针传递。

    package main
    
    import "fmt"
    
    // 通过值传递,仅拷贝变量,不会修改实际值
    func zeroval(ival int) {
        ival = 0
    }
    
    // 通过指针传递,可以修改实际值
    func zeroptr(iptr *int) {
        *iptr = 0
    }
    
    func main() {
        i := 1
        fmt.Println("initial:", i)
    
        zeroval(i)
        fmt.Println("zeroval:", i)
    
    	// 用&获取指针,即变量i的地址
        zeroptr(&i)
        fmt.Println("zeroptr:", i)
    
        fmt.Println("pointer:", &i)
    }
    
    • 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
    $ go run pointers.go
    initial: 1
    zeroval: 1
    zeroptr: 0
    pointer: 0x42131100
    
    • 1
    • 2
    • 3
    • 4
    • 5

    18. Strings and Runes

    下面例子展示了string与rune的关系。string可以看做是由byte元素组成的slice,默认采用UTF-8编码。rune类似于其他语言中的character,它是code point,即该字符在字符表中的唯一序号。注意它与encoding概念不同,encoding代表不同编码方式,如UTF-8、UTF-16,对同一个字符可以使用不同长度的编码来表示。

    package main
    
    import (
        "fmt"
        "unicode/utf8"
    )
    
    func main() {
    
        const s = "สวัสดี"
    
    	// len获取底层byte slice的长度
        fmt.Println("Len:", len(s))
        
    	// 打印底层的每个byte
        for i := 0; i < len(s); i++ {
            fmt.Printf("%x ", s[i])
        }
        fmt.Println()
    
        fmt.Println("Rune count:", utf8.RuneCountInString(s))
    	
    	// 用range遍历,获取的是每个rune及对应的byte起始下标。使用UTF-8编码,每个rune占用3个字节
        for idx, runeValue := range s {
            fmt.Printf("%#U starts at %d\n", runeValue, idx)
        }
    
        fmt.Println("\nUsing DecodeRuneInString")
        for i, w := 0, 0; i < len(s); i += w {
        
        	// 使用DecodeRuneInString也能达到同样的遍历效果
            runeValue, width := utf8.DecodeRuneInString(s[i:])
            fmt.Printf("%#U starts at %d\n", runeValue, i)
            w = width
    
            examineRune(runeValue)
        }
    }
    
    func examineRune(r rune) {
    
        if r == 't' {
            fmt.Println("found tee")
        } else if r == 'ส' {
            fmt.Println("found so sua")
        }
    }
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    $ go run strings-and-runes.go
    Len: 18
    e0 b8 aa e0 b8 a7 e0 b8 b1 e0 b8 aa e0 b8 94 e0 b8 b5 
    Rune count: 6
    U+0E2A 'ส' starts at 0
    U+0E27 'ว' starts at 3
    U+0E31 'ั' starts at 6
    U+0E2A 'ส' starts at 9
    U+0E14 'ด' starts at 12
    U+0E35 'ี' starts at 15
    
    	
    
    Using DecodeRuneInString
    U+0E2A 'ส' starts at 0
    found so sua
    U+0E27 'ว' starts at 3
    U+0E31 'ั' starts at 6
    U+0E2A 'ส' starts at 9
    found so sua
    U+0E14 'ด' starts at 12
    U+0E35 'ี' starts at 15
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    19. Structs

    下面例子展示了用struct关键字实现结构体。结构体是一种自定义类型,属于值变量类型,它可以用于多个变量的封装。

    package main
    
    import "fmt"
    
    // 定义新struct类型,顺序:type、struct名、struct
    type person struct {
        name string
        age  int
    }
    
    func newPerson(name string) *person {
    
    	// 定义struct变量
        p := person{name: name}
        p.age = 42
        return &p
    }
    
    func main() {
    
        fmt.Println(person{"Bob", 20})
    
        fmt.Println(person{name: "Alice", age: 30})
    
        fmt.Println(person{name: "Fred"})
    
        fmt.Println(&person{name: "Ann", age: 40})
    
        fmt.Println(newPerson("Jon"))
    
        s := person{name: "Sean", age: 50}
        fmt.Println(s.name)
    
        sp := &s
        fmt.Println(sp.age)
    
        sp.age = 51
        fmt.Println(sp.age)
    }
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    $ go run structs.go
    {Bob 20}
    {Alice 30}
    {Fred 0}
    &{Ann 40}
    &{Jon 42}
    Sean
    50
    51
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    20. Methods

    下面例子展示了方法的使用。Go非传统面向对象语言,没有其他语言中的class关键字。但可以使用结构体和方法达到类似的效果。与一般的函数不同,方法需要指定struct类型的值或者指针作为接收者,接收者的角色类似于其他语言中的对象。

    package main
    
    import "fmt"
    
    type rect struct {
        width, height int
    }
    
    // 方法定义,顺序:func、接收者、方法名、返回值类型
    // 接收者可以是struct类型的指针
    func (r *rect) area() int {
        return r.width * r.height
    }
    
    // 接受者也可以是struct类型的值
    func (r rect) perim() int {
        return 2*r.width + 2*r.height
    }
    
    func main() {
        r := rect{width: 10, height: 5}
        
    	// go可以自动做struct值类型和指针类型的转换,使得与方法定义适配
        fmt.Println("area: ", r.area())
        fmt.Println("perim:", r.perim())
    
        rp := &r
        fmt.Println("area: ", rp.area())
        fmt.Println("perim:", rp.perim())
    }
    
    • 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
    $ go run methods.go 
    area:  50
    perim: 30
    area:  50
    perim: 30
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    Centos7下安装ruby2.7.8环境、WPScan的安装及使用介绍
    简单测试一下 展锐的 UDX710 性能
    telnet无效指令,telnet找不到命令
    一个无经验的大学毕业生,可以转行做软件测试吗?我的真实案例
    MindSpore术语歧义解释
    物联网时代的等保测评:保障万物互联的安全
    CentOS Linux release 7.9.2009 (Core)中安装配置Tomcat
    OpenCV快速入门:初探
    dubbo和springcloud问题解决——interface not allow null
    使用宝塔部署项目
  • 原文地址:https://blog.csdn.net/needmorecode/article/details/128157495