0°

GO语言系列(五)- 结构体和接口

结构体(Struct)

Go中struct的特点

    1. 用来自定义复杂数据结构
    1. struct里面可以包含多个字段(属性)
    1. struct类型可以定义方法,注意和函数的区分
    1. struct类型是值类型
    1. struct类型可以嵌套
    1. Go语言没有class类型,只有struct类型
    1. Go语言中有tag

一、struct的定义

1.struct的声明


1
2
3
4
5
1type 标识符 struct {
2       field1 type
3       field2 type
4}  
5

例子


1
2
3
4
5
6
1type Student struct {
2       Name string
3       Age int
4Score int
5}
6

2. struct中的tag

Tag是结构体中某个字段别名, 可以定义多个, 空格分隔


1
2
3
4
1type Student struct {
2    Name string `ak:"av" bk:"bv" ck:"cv"`
3}
4

使用空格来区分多个tag,所以格式要尤为注意

tag的作用

tag相当于该字段的一个属性标签, 在Go语言中, 一些包通过tag来做相应的判断

举个例子, 比如我们有一个结构体


1
2
3
4
1type Student struct {
2    Name string
3}
4

然后我们将一个该结构体实例化一个 s1


1
2
3
4
1s1 := Student{
2        Name: "s1",
3    }
4

再将 s1 序列化


1
2
3
4
5
6
1v, err := json.Marshal(s1) // json.Marshal方法,json序列化,返回值和报错信息
2if err != nil { // 不为nil代表报错
3    fmt.Println(err)
4}
5fmt.Println(string(v)) // []byte转string, json
6

此时 string(v) 为 


1
2
3
4
1{
2  "Name": "s1"  
3}
4

因为在 Go 语言中, 结构体字段要想为外部所用就必须首字母大写, 但是如果这个 s1 是返回给前端的, 那每个字段都首字母大写就很怪, 此时我们可以给 Student 加tag解决

结构体修改为


1
2
3
4
1type Student struct {
2    Name string`json:"name"`
3}
4

序列化时, 会自己找到名为 json 的tag, 根据值来进行json后的赋值

因此 string(v) 为


1
2
3
4
1{
2  "name": "s1"  
3}
4

常用tag记录

  • json json序列化或反序列化时字段的名称

  • db sqlx模块中对应的数据库字段名

  • form gin框架中对应的前端的数据字段名

  • binding 搭配 form 使用, 默认如果没查找到结构体中的某个字段则不报错值为空, binding为 required 代表没找到返回错误给前端 

3. struct 中字段访问:和其他语言一样,使用点  


1
2
3
4
5
6
7
8
1var stu Student
2
3stu.Name = “tony”
4stu.Age = 18
5stu.Score=20
6
7fmt.Printf(“name=%s age=%d score=%d”, stu.Name, stu.Age, stu.Score
8

4.  struct定义的三种形式:


1
2
3
4
1a. var stu Student
2b. var stu *Student = new (Student)
3c. var stu *Student = &Student{}
4

其中b和c返回的都是指向结构体的指针,访问形式如下:


1
2
1stu.Name、stu.Age和stu.Score或者 (*stu).Name、(*stu).Age等
2

例子


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
1package main
2
3import "fmt"
4
5type Student struct {
6    Name  string
7    Age   int32
8    score float32 // 外部的包访问不了这个字段
9}
10
11func main() {
12    // 结构体的三种定义方式
13    // 方式一
14    var stu Student
15    stu.Name = "zhangyafei"
16    stu.Age = 24
17    stu.score = 88
18
19    fmt.Printf("Name: %p\n", &stu.Name) // string占10字节
20    fmt.Printf("Age: %p\n", &stu.Age)   // int占8字节  int32占4字节
21    fmt.Printf("score: %p\n", &stu.score)
22
23    // 方式二
24    var stu1 *Student = &Student{
25        Age:  20,
26        Name: "ZhangYafei",
27    }
28    fmt.Println(stu1)
29    fmt.Println(stu1.Name)
30
31    // 方式三
32    var stu2 = Student{
33        Age:  20,
34        Name: "Fei",
35    }
36    fmt.Println(stu2)
37    fmt.Println(stu2.Age)
38
39}
40
41// Name: 0xc000004460
42// Age: 0xc000004470
43// score: 0xc000004478
44
45// Age int32
46// Name: 0xc000050400
47// Age: 0xc000050410
48// score: 0xc000050414
49// &{ZhangYafei 20 0}
50// {Fei 20 0}
51

struct的定义示例

二、struct的初始化

1. struct的内存布局

struct中的所有字段在内存是连续的,布局如下:

2. 链表定义


1
2
3
4
5
1type Student struct {
2    Name string
3    Next* Student
4}
5

每个节点包含下一个节点的地址,这样把所有的节点串起来了,通常把链表中的第一个节点叫做链表头

3. 双链表定义


1
2
3
4
5
6
1type Student struct {
2    Name string
3    Next* Student
4    Prev* Student
5}
6

如果有两个指针分别指向前一个节点和后一个节点,我们叫做双链表

4. 二叉树定义


1
2
3
4
5
6
1type Student struct {
2    Name string
3    left* Student
4    right* Student
5}
6

如果每个节点有两个指针分别用来指向左子树和右子树,我们把这样的结构叫做二叉树

5. 结构体是用户单独定义的类型,不能和其他类型进行强制转换


1
2
3
4
5
6
7
8
9
10
11
12
1type Student struct {
2Number int
3}
4
5type Stu Student //alias
6
7var a Student
8a = Student(30)
9
10var b Stu
11a = b
12

例子


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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
1package main
2
3import (
4    "fmt"
5    "math/rand"
6)
7
8type Student struct {
9    Name  string
10    Age   int
11    Score float32
12    next  *Student
13}
14
15func trans(p *Student) {
16    // 遍历链表
17    for p != nil {
18        fmt.Println(*p)
19        p = p.next
20    }
21}
22
23func insertTail(p *Student) {
24    // 尾插法
25    var tail = p
26    for i := 0; i < 10; i++ {
27        stu := &Student{
28            Name:  fmt.Sprintf("stu%d", i),
29            Age:   rand.Intn(100),
30            Score: rand.Float32() * 100,
31        }
32        tail.next = stu
33        tail = stu
34    }
35}
36
37func insertHead(head **Student) {
38    // 头插法
39    for i := 0; i < 10; i++ {
40        stu := &Student{
41            Name:  fmt.Sprintf("stu%d", i),
42            Age:   rand.Intn(100),
43            Score: rand.Float32() * 100,
44        }
45        stu.next = *head
46        *head = stu
47    }
48}
49
50func delNode(p *Student) {
51    var prev *Student = p
52    for p != nil {
53        if p.Name == "stu6" {
54            prev.next = p.next
55            break
56        }
57        prev = p
58        p = p.next
59    }
60}
61
62func addNode(p *Student, newNode *Student) {
63    for p != nil {
64        if p.Name == "stu6" {
65            newNode.next = p.next
66            p.next = newNode
67            break
68        }
69        p = p.next
70    }
71}
72
73func main() {
74    // var head *Student = &Student{}
75    var head *Student = new(Student)
76    head.Name = "ZhangYafei"
77    head.Age = 2
78    head.Score = 88
79
80    // 尾插
81    // insertTail(head)
82    // 头插
83    insertHead(&head)
84    // 遍历
85    trans(head)
86    // 删除
87    delNode(head)
88    trans(head)
89
90    // 指定位置插入节点
91    var newNode *Student = new(Student)
92    newNode.Name = "newstu"
93    newNode.Age = 34
94    newNode.Score = 100
95    addNode(head, newNod)
96}
97

链表的头插、尾插、遍历、删除和指定位置插入


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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
1package main
2
3import "fmt"
4
5type Student struct {
6    Name  string
7    Age   int
8    Score float32
9    left  *Student
10    right *Student
11}
12
13func PreOrdertrans(root *Student) {
14    if root == nil {
15        return
16    }
17    // 打印这棵树的节点
18    fmt.Println(root)
19    // 递归遍历左子树
20    PreOrdertrans(root.left)
21    // 递归遍历右子树
22    PreOrdertrans(root.right)
23}
24
25func InOrdertrans(root *Student) {
26    if root == nil {
27        return
28    }
29    // 递归遍历左子树
30    InOrdertrans(root.left)
31    // 打印这棵树的节点
32    fmt.Println(root)
33    // 递归遍历右子树
34    InOrdertrans(root.right)
35}
36
37func PostOrdertrans(root *Student) {
38    if root == nil {
39        return
40    }
41    // 递归遍历左子树
42    PostOrdertrans(root.left)
43    // 递归遍历右子树
44    PostOrdertrans(root.right)
45    // 打印这棵树的节点
46    fmt.Println(root)
47}
48func main() {
49    var root *Student = new(Student)
50    root.Name = "Zhangyafei"
51    root.Age = 18
52    root.Score = 88
53
54    var left1 *Student = new(Student)
55    left1.Name = "left1"
56    left1.Age = 18
57    left1.Score = 88
58
59    root.left = left1
60
61    var right1 *Student = new(Student)
62    right1.Name = "right1"
63    right1.Age = 18
64    right1.Score = 88
65
66    root.right = right1
67
68    var left2 *Student = new(Student)
69    left2.Name = "left2"
70    left2.Age = 18
71    left2.Score = 88
72
73    left1.left = left2
74
75    fmt.Println("前序遍历:")
76    PreOrdertrans(root)
77    fmt.Println("中序遍历:")
78    InOrdertrans(root)
79    fmt.Println("后序遍历:")
80    PostOrdertrans(root)
81}
82

二叉树的前、中、后序遍历


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
1package main
2
3import "fmt"
4
5type integer int
6
7type Student struct {
8    Number int
9}
10
11type Stu Student //alias 别名
12
13func main() {
14    var i integer = 1000
15    var j int = 100
16    // 变量操作必须同类型,需要强制转换类型
17    j = int(i)
18    fmt.Println(j)
19
20    var a Student
21    a = Student{30}
22
23    var b Stu
24    a = Student(b)
25    fmt.Println(a)
26}
27

变量的强制类型转换

三、工厂模式

golang中的struct没有构造函数,一般可以使用工厂模式来解决这个问题


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1Package model
2type student struct {
3Name stirng
4Age int
5}
6
7func NewStudent(name string, age int) *student {
8return &student{
9Name:name,
10Age:age,
11}
12}
13
14Package main
15S := new (student)
16S := model.NewStudent(“tony”, 20)
17

 四、struct中的tag

 我们可以为struct中的每个字段,写上一个tag。这个tag可以通过反射的
机制获取到,最常用的场景就是json序列化和反序列化


1
2
3
4
5
1type student struct {
2    Name stirng “this is name field”
3    Age int “this is age field”
4}
5

示例


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
1package main
2
3import (
4    "encoding/json"
5    "fmt"
6)
7
8type Student struct {
9    Name  string `json:"name"` // json打包的时候用name
10    Age   int    `json:"age"`
11    Score int    `json:"score"`
12}
13
14func main() {
15    var stu Student = Student{
16        Name:  "ZhangYafei",
17        Age:   24,
18        Score: 88,
19    }
20    data, err := json.Marshal(stu)
21    if err != nil {
22        fmt.Println("json encoder stu failed, err", err)
23        return
24    }
25    fmt.Println(string(data))
26}
27
28// {"name":"ZhangYafei","age":24,"score":88}
29

json序列化

五、匿名字段

1. 结构体中字段可以没有名字,即匿名字段


1
2
3
4
5
6
7
8
9
10
11
1type Car struct {
2    Name stirng
3    Age int
4}
5
6type Train struct {
7    Car
8    Start time.Time
9    int
10}
11

2. 匿名字段冲突处理


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1type Car struct {
2    Name string
3    Age int
4}
5
6type Train struct {
7    Car
8    Start time.Time
9    Age int
10}
11type A struct {
12    a int
13}
14
15type B struct {
16    a int
17    b int
18}
19
20type C struct {
21    A
22    B
23}
24

示例


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
1package main
2
3import (
4    "fmt"
5    "time"
6)
7
8type Cart1 struct {
9    name string
10    age  int
11}
12
13type Cart2 struct {
14    name string
15    age  int
16}
17
18type Train struct {
19    Cart1
20    Cart2
21    int
22    start time.Time
23    age   int
24}
25
26func main() {
27    var t Train
28    // 访问匿名字段
29    // 方式一
30    t.Cart1.name = "001"
31    t.Cart1.age = 300
32    t.Cart2.name = "002"
33    t.Cart2.age = 400
34    // 方式二
35    // t.name = "train"
36    t.age = 100
37    t.int = 200
38
39    fmt.Println(t)
40}
41

访问匿名字段

六、方法

1. Golang中的方法是作用在特定类型的变量上,因此自定义类型,都可以有方法,而不仅仅是struct


1
2
1定义:func (recevier type) methodName(参数列表)(返回值列表){}
2

2. 方法的调用


1
2
3
4
5
6
7
8
9
10
1type A struct {
2a int
3}
4func (this A) test() {
5fmt.Println(this.a)
6}
7
8var t A
9t.test()
10

3. 方法和函数的区别


1
2
3
1函数调用: function(variable, 参数列表)
2方法:variable.function(参数列表)
3

4. 指针receiver vs 值receiver

本质上和函数的值传递和地址传递是一样的

5. 方法的访问控制,通过大小写控制

6.继承

如果一个struct嵌套了另一个匿名结构体,那么这个结构可以直接访问匿名结构体的方法,从而实现了继承。

7. 组合和匿名字段

如果一个struct嵌套了另一个匿名结构体,那么这个结构可以直接访问匿名结构体的方法,从而实现了继承。如果一个struct嵌套了另一个有名结构体,那么这个模式就叫组合。

8. 多重继承

如果一个struct嵌套了多个匿名结构体,那么这个结构可以直接访问多个匿名结构体的方法,从而实现了多重继承。21. 实现String()

如果一个变量实现了String()这个方法,那么fmt.Println默认会调用这个变量的String()进行输出。

示例


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
1package main
2
3import "fmt"
4
5type integer int
6
7func (p integer) print() {
8    fmt.Println("p is", p)
9}
10
11func (p *integer) set(b integer) {
12    *p = b
13}
14
15type Student struct {
16    Name  string
17    Age   int
18    Score int
19    sex   int
20}
21
22func (p *Student) init(name string, age int, score int) {
23    p.Name = name
24    p.Age = age
25    p.Score = score
26    fmt.Println(p)
27}
28
29func (p Student) get() Student {
30    return p
31}
32
33func main() {
34    var stu Student
35    stu.init("stu", 10, 200)
36    stu1 := stu.get()
37    fmt.Println(stu1)
38
39    var a integer
40    a = 10
41    a.print()
42
43    a.set(1000)
44    a.print()
45}
46

自定义方法


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
1package main
2
3import "fmt"
4
5type Car struct {
6    weight int
7    name   string
8}
9
10func (self *Car) Run() {
11    fmt.Println(self, "is running")
12}
13
14type Bike struct {
15    Car
16    lunzi int
17}
18
19type Train struct {
20    c Car
21}
22
23func main() {
24    var a Bike
25    a.weight = 100
26    a.name = "bike"
27    a.lunzi = 2
28
29    fmt.Println(a)
30    a.Run()
31
32    var b Train
33    b.c.weight = 100
34    b.c.name = "train"
35    b.c.Run()
36}
37

继承


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
1package main
2
3import "fmt"
4
5type Car struct {
6    weight int
7    name   string
8}
9
10func (self *Car) Run() {
11    fmt.Println(self, "is running")
12}
13
14type Bike struct {
15    Car
16    lunzi int
17}
18
19type Train struct {
20    c Car
21}
22
23func (self Train) String() string {
24    str := fmt.Sprintf("name=[%s] weight=[%d]", self.c.name, self.c.weight)
25    return str
26}
27
28func main() {
29    var a Bike
30    a.weight = 100
31    a.name = "bike"
32    a.lunzi = 2
33
34    fmt.Println(a)
35    a.Run()
36
37    var b Train
38    b.c.weight = 100
39    b.c.name = "train"
40    b.c.Run()
41    fmt.Printf("%s", b)
42}
43

实现String方法

接口

一、Go中的接口

1.定义

Interface类型可以定义一组方法,但是这些不需要实现。并且interface不能包含任何变量。


1
2
3
4
5
6
1type example interface{
2    Method1(参数列表) 返回值列表    
3    Method2(参数列表) 返回值列表
4…
5}
6

interface类型默认是一个指针


1
2
3
4
5
6
7
8
9
10
1type example interface{
2
3  Method1(参数列表) 返回值列表
4  Method2(参数列表) 返回值列表
5…
6}
7
8var a example
9a.Method1() 
10

2. 接口实现

  • a. Golang中的接口,不需要显示的实现。只要一个变量,含有接口类型中的所有方法,那么这个变量就实现这个接口。因此,golang中没有implement类似的关键字
  • b. 如果一个变量含有了多个interface类型的方法,那么这个变量就实现了多个接口。
  • c. 如果一个变量只含有了1个interface的方部分方法,那么这个变量没有实现这个接口。

3. 多态

一种事物的多种形态,都可以按照统一的接口进行操作

4. 接口嵌套


1
2
3
4
5
6
7
8
9
10
11
12
13
14
1type ReadWrite interface {
2               Read(b Buffer) bool
3               Write(b Buffer) bool
4}
5type Lock interface {
6               Lock()
7               Unlock()
8}
9type File interface {
10               ReadWrite
11               Lock
12               Close()
13}  
14

二、类型断言

1. 类型断言

由于接口是一般类型,不知道具体类型,如果要转成具体类型可以采用以下方法进行转换:


1
2
3
4
5
6
7
8
9
1var t int
2var x interface{}
3x = t
4y = x.(int)   //转成int
5var t int
6var x interface{}
7x = t
8y, ok = x.(int)   //转成int,带检查
9

2. 练习,写一个函数判断传入参数的类型


1
2
3
4
5
6
7
8
9
10
11
12
1func classifier(items ...interface{}) {
2          for i, x := range items {
3                  switch x.(type) {
4                   case bool:       fmt.Printf(“param #%d is a bool\n”, i)
5                   case float64:    fmt.Printf(“param #%d is a float64\n”, i)
6                   case int, int64: fmt.Printf(“param #%d is an int\n”, i)
7                   case nil: fmt.Printf(“param #%d is nil\n”, i)
8                   case string: fmt.Printf(“param #%d is a string\n”, i)
9                    default: fmt.Printf(“param #%d’s type is unknown\n”, i)
10            }
11}
12

3. 类型断言,采用type switch方式

4.空接口

空接口没有任何方法,所以所有类型都实现了空接口。Interface{}


1
2
3
4
1var a int
2var b interface{}
3b = a 
4

示例


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
52
53
54
55
56
57
58
1package main
2
3import "fmt"
4
5type People struct {
6    name string
7    age  int
8}
9
10type Test interface {
11    Print()
12    Sleep()
13}
14
15type Student struct {
16    name  string
17    age   int
18    score int
19}
20
21func (self *Student) Print() {
22    fmt.Println("name:", self.name)
23    fmt.Println("age:", self.age)
24    fmt.Println("score:", self.score)
25}
26
27func (self People) Print() {
28    fmt.Println("name:", self.name)
29    fmt.Println("age:", self.age)
30}
31
32func (self People) Sleep() {
33    fmt.Println("people is sleep")
34}
35
36func (self Student) Sleep() {
37    fmt.Println("student is sleep")
38}
39
40func main() {
41    var t Test
42    var stu Student = Student{
43        name:  "Zhangyafei",
44        age:   24,
45        score: 88,
46    }
47    t = &stu
48    t.Print()
49
50    var people People = People{
51        name: "people",
52        age:  24,
53    }
54    t = people
55    t.Print()
56    t.Sleep()
57}
58

接口示例

扩展:实现一个图书管理系统,具有以下功能:

  • a. 书籍录入功能,书籍信息包括书名、副本数、作者、出版日期
  • b. 书籍查询功能,按照书名、作者、出版日期等条件检索
  • c. 学生信息管理功能,管理每个学生的姓名、年级、身份证、性别、借了什么书等信息
  • d. 借书功能,学生可以查询想要的书籍,进行借出

参考


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
1package model
2
3import (
4    "errors"
5    "time"
6)
7
8var (
9    ErrStockNotEnough = errors.New("stock is not enough")
10)
11
12type Book struct {
13    Name       string
14    Total      int
15    Author     string
16    CreateTime time.Time
17}
18
19func CreateBook(name string, total int, author string, createTime time.Time) (b *Book) {
20    b = &Book{
21        Name:       name,
22        Total:      total,
23        Author:     author,
24        CreateTime: createTime,
25    }
26    return
27}
28
29func (self *Book) canBorrow(c int) bool {
30    return self.Total >= c
31}
32
33func (self *Book) Borrow(c int) (err error) {
34    if self.canBorrow(c) == false {
35        err = ErrStockNotEnough
36        return
37    }
38    self.Total -= c
39    return
40}
41
42func (self *Book) Back(c int) (err error) {
43    self.Total += c
44    return
45}
46

book.go


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
52
53
54
55
56
57
58
59
1package model
2
3import (
4    "errors"
5)
6
7var (
8    ErrNotFoundBook = errors.New("not found book")
9)
10
11type Student struct {
12    Name  string
13    Grade string
14    Id    string
15    Sex   string
16    books []*BorrowItem
17}
18
19type BorrowItem struct {
20    book *Book
21    num  int
22}
23
24func CreateStudent(name, grade, id, sex string) *Student {
25    stu := &Student{
26        Name:  name,
27        Grade: grade,
28        Id:    id,
29        Sex:   sex,
30    }
31    return stu
32}
33
34func (self *Student) AddBook(b *BorrowItem) {
35    self.books = append(self.books, b)
36}
37
38func (self *Student) DelBook(b *BorrowItem) (err error) {
39    for i := 0; i < len(self.books); i++ {
40        if self.books[i].book.Name == b.book.Name {
41            if b.num == self.books[i].num {
42                front := self.books[0:i]
43                left := self.books[i+1:]
44                front = append(front, left...)
45                self.books = front
46                return
47            }
48            self.books[i].num -= b.num
49            return
50        }
51    }
52    err = ErrNotFoundBook
53    return
54}
55
56func (self *Student) GetBookList() []*BorrowItem {
57    return self.books
58}
59

stu.go

 

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!