官方文档中明确指出,Slice(切片)的零值是 nil,在没有明确初始化的情况下这是显而易见的,任何 Go 开发者应该都知道才对。
但是对于 GoLand 而言,声明并直接初始化的空 Slice 和声明不初始化 Slice 是一回事。为什么这么说呢?如果你在 GoLand 中写入以下两句代码:
var sliceNoInit []string
sliceInit := []string{}
第二行代码的等号右边会有波浪线,提示这种写法可以被 Cleanup。如果你根据 IDE 提示按 Alt +Enter 那么第二行代码会被重构成:
var sliceInit []string
你没有看错,GoLand 将一个初始化过的 Slice 重构为了未初始化的的 Slice…… GoLand 认为前者是冗余的写法。的确,大部分函数对于 nil Slice 和 empty Slice 而言,都是一样的,例如它们的长度都是 0。不过
nil Slice 的长度为 0,所以长度为 0 的都可以转变 nil Slice
这样考虑就有问题了。这有什么问题吗?它们两个长度不都是零么?当然有问题,在 len 函数的注释清晰的写了这么一句话:
if v is nil, len(v) is zero
这是什么意思呢?就是说 len 函数允许参数为 nil,但只要参数为 nil 不管任何类型都会返回 0。所以 nil Slice 的长度为零是一种不严禁的说法,应该说调用 len 函数的结果为零。
所以,初始化后空的 Slice 是绝对不等同于没有初始化的 nil Slice 的。最直接的,它们对 slice == nil 的结果就相反。
带来的麻烦
在使用某些 JSON 框架的时候,nil Slice 会被解析为 null 而不是 [],如果你有 web 开发经验就知道这是个多么严重的小问题了。而我恰好就是遇到这个问题了,最后追查原因原来是我手动初始化的空 Slice 被重构成了未初始化 nil Slice,直接导致了这个转换为 null 的结果。
我们用伪代码举例(不涉及具体框架):
var list []string
if err,result := QueryAll();err== nil{
list = result.Rows
}
ToJson(map[string]interface{}{
“list”: list,
})
有没有看出什么问题?如果 QueryAll 返回错误的话,那么 list 永远不会被初始化,则很有可能在转换时被解析为 null 而不是 []。如果你想这么写解决这个弊端:
list := []string{}
那么你会被 GoLand 活活纠正成上面那样…… 然后带来错误的结果。
解决办法
进入 Settings -> Editor - Inspections
展开 Go - Declaration redundancy
将 Empty slice declared via literal 右边的勾去掉
这么做可以关掉 GoLand 对直接初始化的空 Slice 的冗余检查。
不过有意思的是还有一种以代码的方式杜绝这种提示,那就是使用 make 函数:
list := make([]string, 0)
实际上使用 make 创建一个长度为 0 的 empty Slice 和不插入一个值的直接初始化这两者才是等价的…… 但是 GoLand 却不会将 make 函数这种初始化方式算做“冗余”写法。
[推荐](https://zhuanlan.zhihu.com/p/35777565)