记一个有趣的bug
Contents
[NOTE] Updated January 1, 0001. This article may have outdated content or subject matter.
先看代码
package main
import (
"fmt"
)
type bug struct {
name string
cause string
}
func main() {
bugNames := []string{"an", "interesting", "bug"}
var bugs []*bug
bugObj := &bug{
cause: "sth",
}
for idx, bn := range bugNames {
bugs = append(bugs, bugObj)
bugs[idx].name = bn
}
for _, b := range bugs {
fmt.Printf("bug:%v", *b)
}
}
其实,不难看出,这段代码想要的结果是:bug:{an sth}bug:{interesting sth}bug:{bug sth}
,但是结果却变成了:bug:{bug sth}bug:{bug sth}bug:{bug sth}
why ?
可能熟悉指针指针的同学一眼就看出来了,这么写是显然不合理的,因为在循环外部的bugObj
这里不是一个结构体,而是一个结构体指针,所以,在循环里面bugs[idx]
其实操作的一直是同一个bugObj
,也就是说,这边的更新操作是不成立的。
具体点说,可以尝试说明下这个循环里面都发生了什么:
- idx == 0; bugs[0].name = “an” =>
[]*bug{&bug{name:"an",cause:"sth"}}
— √ - idx == 1; bugs[1].name = “interesting” =>
[]*bug{&bug{name:"interesting",cause:"sth"},&bug{name:"interesting",cause:"sth"}}
— x(在这次操作中,bugs[0](idx == 0)
和bugs[1](idx == 1)
其实指向的是同一个bugObj
) - idx == 2; bugs[2].name = “bug” => 同上
改正
改正其实也很简单,不改变原需求的情况下,把初始化bugObj那段代码移动到循环里面来就是了。
也就是:
package main
import (
"fmt"
)
type bug struct {
name string
cause string
}
func main() {
bugNames := []string{"an", "interesting", "bug"}
var bugs []*bug
for _, bn := range bugNames {
bugObj := &bug{
name: bn,
cause: "sth",
}
bugs = append(bugs, bugObj)
}
for _, b := range bugs {
fmt.Printf("bug:%v", *b)
}
}
随想
滥用指针不是什么好事情,有指针机制可能会让语言很灵活,但是用之前(至少在Code Review的时候)也要先想想清楚,之前跟SY大佬(和Go TG Bot API群的同学)讨论过在TG BOT SDK里面很多地方传的都是结构体数组,而不是结构体指针数组的问题,得到的回答是it is enough
。确实是让人无法反驳但是又没啥错的回答呢。虽然这不关上面的那个bug啥事,但是只是想说 好用的东西也不要去滥用。
以上。来自一只很久很久没有更新博客的菜鸡。
Author scnace
LastMod 0001-01-01