Go 易采坑总结

汇总一些本人积累的Go语言中容易犯的错误(持续更新)

交锋过的

nil的比较

在Go语言中,一个interface{}类型的变量包含两个指针,一个指向其类型,另一个指向真正的值。所以对于一个interface{}类型的nil变量来说,它的两个指针都是0。这是符合Go语言对nil的标准定义的。

当我们将一个具体类型的值赋值给一个interface类型的变量的时候,就同时把类型和值都赋值给了interface里的两个指针。如果这个具体类型的值是nil的话,interface变量依然会存储对应的类型指针和值指针。这个时候拿这个interface变量去和nil常量进行比较的话就会返回false

1
2
3
4
5
6
7
8
func main() {
var p *int = nil
var i interface{} = p
fmt.Println(i == nil)
}
结果:
false

这个坑可以采取避免将一个有可能为nil的具体类型的值赋值给interface变量尽量避免

json中反序列化丢失精度的问题

在json的规范中,对于数字类型是不区分整形和浮点型的。在使用json.Unmarshal进行json的反序列化的时候,如果没有指定数据类型(单纯使用interface{}作为接收变量)可能存在丢失精度的问题。因为如果没有指定具体的数据类型,其默认采用的float64作为其数字的接受类型,当数字的精度超过float能够表示的精度范围时就会造成精度丢失的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func main() {
const jsonStream = `{"key": 675667567896876867896}`
var user interface{} // 不指定反序列化的类型
err := json.Unmarshal([]byte(jsonStream), &user)
if err != nil {
fmt.Println("error:", err)
}
m := user.(map[string]interface{})
finalStr, err:= json.Marshal(user)
if err != nil {
fmt.Println("error:", err)
}
fansCount := m["key"]
fmt.Println(string(finalStr))
fmt.Printf("%+v \n", reflect.TypeOf(fansCount).Name())
fmt.Printf("%+v \n", fansCount.(float64))
}
Output:
{"key":675667567896876800000}
float64
6.756675678968768e+20

解决方法是使用func (*Decoder) UseNumber方法告诉反序列化JSON的数字类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func main() {
const jsonStream = `{"key": 675667567896876867896}`
var user interface{} // 不指定反序列化的类型
decoder := json.NewDecoder(strings.NewReader(jsonStream))
decoder.UseNumber()
err := decoder.Decode(&user)
if err != nil {
fmt.Println("error:", err)
}
finalStr, err:= json.Marshal(user)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(finalStr))
}
Output:
{"key":675667567896876867896}

初级

使用range对String迭代

使用range迭代String的时候要注意,迭代的索引是你迭代的字符的第一个byte(这个字符可能是unicode字符/rune),所以这个索引值不一定是一个自然数的序列。

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
data := "A\xfe\x02\xff\x04"
for _,v := range data {
fmt.Printf("%#x ",v)
}
//prints: 0x41 0xfffd 0x2 0xfffd 0x4 (not ok)
fmt.Println()
for _,v := range []byte(data) {
fmt.Printf("%#x ",v)
}
//prints: 0x41 0xfe 0x2 0xff 0x4 (good)
}

没有导出的Struct字段将无法encoded

在struct中小写字母开头的字段将无法encoded,此时你进行encode的时候这些字段将会是零值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type MyData struct {
One int
two string
}
func main() {
in := MyData{1,"two"}
fmt.Printf("%#v\n",in) //prints main.MyData{One:1, two:"two"}
encoded,_ := json.Marshal(in)
fmt.Println(string(encoded)) //prints {"One":1}
var out MyData
json.Unmarshal(encoded,&out)
fmt.Printf("%#v\n",out) //prints main.MyData{One:1, two:""}
}

参考: