让 iota 从 a +1 开始增量《这是一种误导,是一种错误逻辑》
我在网上看到了一篇《十条有用的 GO 技术》其实是很多篇,该死的爬虫。
下面的内容都是误导别人的错误逻辑,可能是原创作者当时入坑不深,我发表此文章的本意仅仅是为了讨论技术,可能会使某人不开心,但一个正确的技术答案,是所有人想要的。
其中第六条这样解释:
6. 让 iota 从 a +1 开始增量
在前面的例子中同时也产生了一个我已经遇到过许多次的 bug。假设你有一个新的结构体,有一个 State 字段:type T struct { Name string Port int State State }
现在如果基于 T 创建一个新的变量,然后输出,你会得到奇怪的结果 (http://play.golang.org/p/LPG2RF3y39) :func main() { t := T{Name: "example", Port: 6666} // prints: "t {Name:example Port:6666 State:Running}" fmt.Printf("t %+vn", t) }
看到 bug 了吗?State 字段没有被初始化,Go 默认使用对应类型的零值进行填充。由于 State 是一个整数,零值也就是 0,但在我们的例子中它表示 Running。
如何知道 State 被初始化了?如何得知是在 Running 模式?事实上,没有很好的办法区分它们,并且它们还会产生更多未知的、不可预测的 Bug。不过,修复这个很容易,只要让 iota 从 +1 开始 (http://play.golang.org/p/VyAq-3OItv):const ( Running State = iota + 1 Stopped Rebooting Terminated )
现在 t 变量将默认输出 Unknown,是不是?func main() { t := T{Name: "example", Port: 6666} // 输出: "t {Name:example Port:6666 State:Unknown}" fmt.Printf("t %+vn", t) }
不过让 iota 从零值开始也是一种解决办法。例如,你可以引入一个新的状态叫做 Unknown,将其修改为:const ( Unknown State = iota Running Stopped Rebooting Terminated )
OK,上面的内容都是误导别人的错误逻辑,可能是原创作者当时入坑不深,我发表此文章的本意仅仅是为了讨论技术,可能会使某人不开心,但一个正确的技术答案,是所有人想要的。
开始正式讨论
先放一段上面的代码
type T struct {
Name string
Port int
State State
}
type State int
const (
Running State = iota //iota将const常量依次绑定了数值0,1,2,3
Stopped
Rebooting
Terminated
)
//我们这里重写了string官方函数,
//使得State类型的打印结果不是01234... 而是指定的字符串。
func (s State) String() string {
switch s {
case Running:
return "Running"
case Stopped:
return "Stopped"
case Rebooting:
return "Rebooting"
case Terminated:
return "Terminated"
default:
return "Unknown"
}
}
func main() {
t := T{Name: "example", Port: 6666}
fmt.Printf("t %+v\n", t)
}
//t {Name:example Port:6666 State:Running}
我们看一下有疑问的地方 main
里的 fmt.Printf("t %+v\n", t)
t 结构体的第三个字段没有赋值,会默认赋予零值。
但是为什么T.State打印出来是Running,这是正确的,请看我的解释:
❌有人会这样想:结果是出乎意料的,T.State的打印结果应该是0。因为State 是一个整数,零值也就是 0,不应该是Running。。。。这样的逻辑思想是错误的。
2019年11月08日11:37:22新增解释:\
温故而知新:代码中State实现了string接口,而T又包含了State,因此T也实现了string接口,所以main程序里T会调用重写后的string函数。
解决:
1️⃣可以使用标准库的string打印:fmt.Printf("t %#v\n", t)
2️⃣或者我们去掉自定义的string方法即可。
3️⃣或者修改string方法的接收器
4️⃣或者修改T.State类型
✅总之,在string判断中,只要实现了string接口,则一定会调用重构的string。
✅stackoverflow论坛的回答:(我提问这个问题之后,被很多人鄙视我。。。)
T.State的类型为State。这与字段T无关。State不是T(struct)的一部分,仅仅是成员。
T.State类型的零值为0,也等于Running。
因此,当您分配给T的实例且T.State未初始化时,T.State会得到零值,即
Running
✅github,GO官方人员回答:(有幸得到了官方的回复)
就像@DisposaBoy所说的,这是因为State的任何整数值在打印时都会被字符串化%+v,在这种情况下,设置了State的零值,所以Running将其打印出来。参见https://yourbasic.org/golang/iota/
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: