前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go语言IDE GoLand的BUG

Go语言IDE GoLand的BUG

作者头像
我的小碗汤
发布2018-09-30 10:32:31
1.4K0
发布2018-09-30 10:32:31
举报
文章被收录于专栏:我的小碗汤我的小碗汤

前言:

GoLand 是 Jetbrains 推出的 Golang IDE,在内侧阶段我就开始使用了,刚出的时候我还在博客中发表过文章(看了下日期是 16 年年底)。

那时候它还不是很完善,BUG 很多。准确的说也不算 BUG,主要是语法提示上的各种不足,重构功能也很弱。后来我有一段时间没有写 Go 代码,直到它更新为正式版我才差不多又抽出机会继续写 Go 代码了。虽然它已经很完善了,但还是发现它的一个很小但又很明显的 BUG,不过这个 BUG 却恰好给我造成了麻烦,所以我才想发文描述一下。

有关零值:

When storage is allocated for a variable, either through a declaration or a call of new, or when a new value is created, either through a composite literal or a call of make, and no explicit initialization is provided, the variable or value is given a default value. Each element of such a variable or value is set to the zero value for its type: false for booleans, 0 for numeric types, “” for strings, and nil for pointers, functions, interfaces, slices, channels, and maps. This initialization is done recursively, so for instance each element of an array of structs will have its fields zeroed if no value is specified.

官方文档中明确指出,Slice(切片)的零值是 nil,在没有明确初始化的情况下这是显而易见的,任何 Go 开发者应该都知道才对。

但是对于 GoLand 而言,声明并直接初始化的空 Slice 和声明不初始化 Slice 是一回事。为什么这么说呢?如果你在 GoLand 中写入以下两句代码:

代码语言:javascript
复制
var sliceNoInit []string
sliceInit := []string{}

第二行代码的等号右边会有波浪线,提示这种写法可以被 Cleanup。如果你根据 IDE 提示按 Alt +Enter 那么第二行代码会被重构成:

代码语言:javascript
复制
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 的结果。

我们用伪代码举例(不涉及具体框架):

代码语言:javascript
复制
var list []string
if err,result := QueryAll();err== nil{
  list = result.Rows
}
ToJson(map[string]interface{}{
  "list": list,
})

有没有看出什么问题?如果 QueryAll 返回错误的话,那么 list 永远不会被初始化,则很有可能在转换时被解析为 null 而不是 []。

如下代码:

代码语言:javascript
复制
package main

import (
  "encoding/json"
  "github.com/pkg/errors"
  "fmt"
)

type result struct {
  Rows []string
  Columes []string
}

func main() {
  //1、var list []string

  //2、list := make([]string, 0)
  list := make([]string, 0)

  //3、list := []string{}

  if err, result := QueryAll(); err == nil {
    list = result.Rows
  }
  ss, err := json.Marshal(map[string]interface{}{
    "list": list,
  })

  fmt.Printf("ss:%s, error:%v", string(ss), err)
}

func QueryAll() (error, result) {
  return errors.New("error"), result{}
}

如果用第一种方式初始化:

代码语言:javascript
复制
var list []string

结果为:

代码语言:javascript
复制
ss:{"list":null}, error:<nil>

用第二种和第三种方式初始化:

代码语言:javascript
复制
list := make([]string, 0)
list := []string{}

结果为:

代码语言:javascript
复制
ss:{"list":[]}, error:<nil>

如果你想这么写解决这个弊端:

代码语言:javascript
复制
list := []string{}

那么你会被 GoLand 活活纠正成上面那样…… 然后带来错误的结果。

真实细节:

在 GoLand 的博客中有一篇文章提到了关于 Slice 的零值,他们是这么认为的:

a nil slice is functionally equivalent to a zero-length slice, even though it points to nothing

原文在这里:https://blog.golang.org/slices

而造成 gin 的 JSON 方法将 nil Slice 转换 null 的原因是 json.Marshal() 就是这么处理的,也就是说几乎任何涉及到 JSON 处理的地方都有可能因为 GoLand 团队认为二者在功能上完全等价而造成问题。

解决办法:

进入 Settings -> Editor - Inspections

展开 Go - Declaration redundancy

将 Empty slice declared via literal 右边的勾去掉

这么做可以关掉 GoLand 对直接初始化的空 Slice 的冗余检查。

不过有意思的是还有一种以代码的方式杜绝这种提示,那就是使用 make 函数:

代码语言:javascript
复制
list := make([]string, 0)

实际上使用 make 创建一个长度为 0 的 empty Slice 和不插入一个值的直接初始化这两者才是等价的…… 但是 GoLand 却不会将 make 函数这种初始化方式算做“冗余”写法。

结束语:

本文来自知乎:

https://zhuanlan.zhihu.com/p/35777565

刚好今天遇到这个goland这个警告,顺便学习记录下,侵删!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-09-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 进击云原生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档