1、可变参数是空接口类型
func main() {
var a = []interface{}{1, 2, 3}
fmt.Println(a)
fmt.Println(a...)
不管是否展开,编译器都无法发现错误,但是输出是不同的:
[1 2 3]
1 2 3
2、数组是值传递
在函数调用参数中,数组是值传递,无法通过修改数组类型的参数返回结果,因此必要时使用切片
3、切片的函数传参
Go语言中,如果以切片为参数调用函数时,有时候会给人一种参数采用了传引用的方式的假象:因为在被调用函数内部可以修改传入的切片的元素。其实,任何可以通过函数参数修改调用参数的情形,都是因为函数参数中显式或隐式传入了指针参数。函数参数传值的规范更准确说是只针对数据结构中固定的部分传值,例如字符串或切片对应结构体中的指针和字符串长度结构体传值,但是并不包含指针间接指 向的内容。将切片类型的参数替换为类似 reflect.SliceHeader 结构体就很好理解切片传值的含义了:
func twice(x []int) {
for i := range x {
x[i] *= 2
}
}
type IntSliceHeader struct {
Data []int
Len int
Cap int
}
func twice(x IntSliceHeader) {
for i := 0; i < x.Len; i++ {
x.Data[i] *= 2
}
}
如果只是修改切片中的元素值,那么函数参数直接传切片即可,但如果要修改切片的长度,要增加或者减少切片中的成员,则需要传入切片的指针或者通过返回值返回修改后的切片
func modifySlice(array *[]int, elem int) {
*array = append(*array, elem)
}
func modifySlice(array []int, elem int) []int {
array = append(array, elem)
return array
}
因为切片中的底层数组部分是通过隐式指针传递(指针本身依然是传值的,但是指针指向的却是同一份的数据),所以被调用函数是可以通过指针修改掉调用参数切片中的数据。除了数据之外,切片结构还包含了切片长度和切片容量信息,这2个信息也是传值的。如果被调用函数中修改了 Len 或 Cap 信息的话,就无法反映到调用参数的切片中,这时候我们一般会通过返回修改后的切片来更新之前的切片。这也是为何内置的 append 必须要返回一个切片的原因。
4、map遍历顺序不固定
map是一种hash表的实现,每次遍历的顺序可能都不一样
5、返回值被屏蔽
在局部作用域中,命名的返回值会被同名的局部变量屏蔽
func Foo() (err error) {
if err := Bar(); err != nil {
return
}
return
}
6、recover必须在defer函数中运行
recover捕获的是祖父级调用时的异常,直接调用时无效:
func main() {
recover()
panic(1)
}
直接defer调用也是无效的:
func main() {
defer recover()
panic(1)
}
defer调用时多层嵌套依然无效:
func main() {
defer func() {
func() { recover() }()
}()
panic(1)
}