基础知识

值类型&引用类型

go数据结构中包含值类型和引用类型两种:

引用类型:

  • slice
  • map
  • channel
  • interface
  • function

值类型:其他基本数据结构

区别

基本类型:变量直接存储值,内存通常在栈中分配,栈在函数调用完会被释放

引用类型:变量存储的是一个地址,这个地址存储最终的值。内存通常在 堆上分配。通过 GC 回收。

引用类型的实现中,包含了对底层数据结构的引用,比如slice底层是由数组实现的,一个slice实际上包含了对底层数据的引用。

make&new

new(T):开辟内存,置为0值,返回指针。T可以是各种数据结构。

make:只能传入chan、map 以及 slice,返回原值,非指针

for range中循环变量的坑

众所周知,go中使用for range语法得到的循环变量,并不是原始数据,而是原始数据的副本。因此我们常见的说法是,使用for range语法修改原始数据是无效的。举个例子

type person struct {
        name string
        age  int
}
people := []person{
  {name: "Alice", age: 20},
  {name: "Bob", age: 25},
}

for _, p := range people {
  p.age += 10
}

// age还是20, 25
t.Log(people[0], people[1]) 

(go 1.21开启了for range循环变量语义的修改测试)

for range+引用类型

但是,当我们for loop中的循环变量结构体中包含引用类型时,对这些引用类型的修改,还是会同步到原始数据中的。从实现原理上易得:引用类型即使复制了副本,但是这些副本存储的都是地址(门牌号),最终找到的数据全是同一份,因此修改任意一个都会影响其他的。

func TestForLoop(t *testing.T) {

    type person struct {
        name string
        age  int
        data map[string]string
    }
    people := []person{
        {name: "Alice", age: 20, data: map[string]string{"a": "1"}},
        {name: "Bob", age: 25, data: map[string]string{"a": "1"}},
    }

    for _, p := range people {
        p.age += 10
        p.data["a"] = "2"
        p.data["b"] = "3"
    }

  // age还是20, 25,但是data都被改了
    t.Log(people[0], people[1]) 
}

应用

修改pod中的container

比如在kubernetes的相关开发中,有时候需要对container进行修改,先来看pod中和container相关的定义

// PodSpec is a description of a pod.
type PodSpec struct {
  Containers []Container
}

type Container struct {
  Name string
  Resources ResourceRequirements
}
type ResourceRequirements struct{
  Limits ResourceList
  Requests ResourceList
}
type ResourceList map[ResourceName]resource.Quantity

可以看到Container的Name是基本类型,而Container的ResourceList是map。因此,如果用for range同时去修改Container中的Name和ResourceList,结果对于pod中原容器的影响是不同的。Name并不会改变,而ResourceList这个map是可以被修改的。

结构定义

已知:

  1. go中函数参数是传值的
  2. 指针省空间,效率高

我们看k8s的代码中对数据结构的定义,可以看到slice中放结构体或者map中存结构体时,大多都是直接使用[]T及map[string]T,而不是[]*T及map[string]*T。理解了引用类型的原理,就可以解释这一点了:因为没有必要再为门牌号准备门牌号。

参考

Go 1.21 Release Candidate - The Go Programming Language

文章目录