1 首先go中所有的参数传递都是值类型!

三个特殊的数据结构:slice map Chan,可以理解为特殊的值类型。 slice、map和chan看上去像引用只是因为他们内部有指针或本身就是指针而已。而对于type struct 自定义类型,如果不想传值可以使用传指针。

type slice struct {
array unsafe.Pointer
len int
cap int
}
slice结构体里有一个指向底层数组array的指针,所以slice在作为函数参数传递进去的时候,虽然和map以及chan一样可以修改其中的值,但是内部slice若使用append之类的方法修改了大小,则这部分长度信息的变化不会反馈到外层slice中,甚至会因为底层数组扩容导致内外slice指向了不同的底层数组,进而后续的所有修改也将不会再影响到外部,使用的时候一定要小心;而map和chan因为本质上就是指针,故所有函数内的变动都会反馈到外面,除非在函数内部改变了这些指针指向的内存.

2 数组的抽象层slice

一句话,永远将slice考虑成引用类型。
如:拼接方法

func Concat(s1, s2 []int) (r []int) {
	r = make([]int, len(s1)+len(s2))
	copy(r, s1)
	copy(r[len(s1):], s2)
	return r
}

3 slice和map使用时别掉到坑里,如下

    s1 := []int{1, 2, 3, 4}
	s2 := s1[1:2]
	s2 = append(s2, 42)
	s2 = append(s2, 43)
	s2 = append(s2, 44)
	s2 = append(s2, 45)

	fmt.Printf("%v\n", s1) // [1 2 42 43]
	fmt.Printf("%v\n", s2) // [2 42 43 44 45]

// 错误方式 如下
func main() {
	var result map[int]int
	makeNewMap(result)  // 传递的是nil ,函数调用达不到预期
}
func makeNewMap(result map[int]int) {
	result = make(map[int]int)
	result[0] = 0
}

// 正确方式 如下
func main() {
	var result map[int]int
	makeNewMap(&result)  // 传递指针
	result2 := makeNewMapV2()
}
func makeNewMap(result *map[int]int) {
	*result = make(map[int]int)
	(*result)[0] = 0
}
func makeNewMapV2() (result map[int]int) {
	result = make(map[int]int)
	result[1] = 1
	return result
}
小结
  1. go语言里尽量用指针存放结构体,可以避免很多值拷贝,也能避免map类型的value不能取址的问题
  2. go里的nil含义不仅仅代表空指针,它还可以代表slice这种类型的空结构体,这部分是go底层采用特殊处理的方式实现的
  3. go用指针,但指针可能是nil的,引用则代表一定存在值,注意两者的区别。通常来说使用指针前都应该判空,这是个好习惯,但这样在go函数体里充满判空语句也会显得比较繁琐。
  4. 闭包中的引用如何理解呢?