最近在组内review代码时经常看到一些比较别扭的写法,感觉用Go语言中的comma ok语法能够很好的解决。这里结合3个场景对其用法进行一下简单总结:
清晰的map访问
首先看一下不使用comma ok的一般写法:
1 | m := make(map[string]int) |
按照以上写法,当m中不存在k这个key时,v的值是自身类型对应的“零值”。如int类型的零值为0,string类型的零值为""
,slice、map、func、channel和指针类型的零值为nil。
假如以上示例中v的值为0,我们无法区分是因为以下两种情况中的哪一种造成的:
- m中存在k,并且对应的值就是0;
- m中根本不存在k,v实际上被赋值为“零值”。
针对以上情况,有的同学将map的value类型改成了指针,通过判断是否为nil来区分。这样会造成额外的内存分配,而且实现方式也不优雅。
再来看一下使用comma ok语法能有什么不同:
1 | v, ok := m["k"] |
无论以上示例中v的值是否为0,ok的真假状态直接对应key是否存在:
- 如果ok为false,表示m中不存在k;
- 如果ok为true,表示m中存在k。
安全的类型断言
首先来看一下不使用comma ok的类型断言:
1 | i := 10 |
以上实例中a的实际类型为int,无法通过类型断言转换为string,所以会因断言失败而panic。
再看一下comma ok语法的方式:
1 | s, ok := a.(string) |
以上示例中:
- 断言失败时不会panic,ok为false,s为string类型的“零值”,即
""
; - 断言成功时,ok为true,s被赋值为a底层的string。
channel receive
Go语言的channel被设计为能够用作“事件通知”,具体实现就是在close之后可以无限receive,不会阻塞并且得到的值都是对应元素类型的“零值”。
首先看一下普通的channel receive语法:
1 | ch := make(chan int) |
如果以上示例中i等于0,我们无法判断:
- 通道ch中确实传递过来一个0;
- 因为ch已经被关闭,所以我们得到一个“零值”。
再来看一下comma ok语法:
1 | i, ok := <-ch |
如果因为ch关闭而得到一个“零值”,ok会是false。
对于类似如下的有缓冲channel,如果在其被关闭后缓冲中还有数据,此时comma ok得到的ok会是true,直到缓冲数据读尽变成false:
1 | ch := make(chan int, 10) |