引用
Effective Go - The Go Programming Language (google.cn)
初始化 |《高效的 Go 编程 Effective Go 2020》| Go 技术论坛 (learnku.com)
尽管从表面上看,Go 的初始化过程与 C 或 C++ 差别并不算太大,但它确实更为强大。在初始化过程中,不仅可以构建复杂的结构,还能正确处理不同包对象间的初始化顺序。
Constants 常量
Go 中的常量就是不变量。它们在编译时创建,即便它们可能是函数中定义的局部变量。 常量只能是数字、字符(符文)、字符串或布尔值。由于编译时的限制, 定义它们的表达式必须也是可被编译器求值的常量表达式。例如 1<<3 就是一个常量表达式,而 math.Sin(math.Pi/4) 则不是,因为对 math.Sin 的函数调用在运行时才会发生。
在 Go 中,枚举常量使用枚举器 iota 创建。由于 iota 可为表达式的一部分,而表达式可以被隐式地重复,这样也就更容易构建复杂的值的集合了。
typeByteSizefloat64const(_=iota//ignorefirstvaluebyassigningtoblankidentifierKBByteSize=1<<(10*iota)MBGBTBPBEBZBYB)
用户定义的类型可以重写String方法, 因此它就为打印时自动格式化任意值提供了可能性,即便是作为一个通用类型的一部分。 尽管你常常会看到这种技术应用于结构体,但它对于像 ByteSize 之类的浮点数标量等类型也是有用的。
func(bByteSize)String()string{switch{caseb>=YB:returnfmt.Sprintf("%.2fYB",b/YB)caseb>=ZB:returnfmt.Sprintf("%.2fZB",b/ZB)caseb>=EB:returnfmt.Sprintf("%.2fEB",b/EB)caseb>=PB:returnfmt.Sprintf("%.2fPB",b/PB)caseb>=TB:returnfmt.Sprintf("%.2fTB",b/TB)caseb>=GB:returnfmt.Sprintf("%.2fGB",b/GB)caseb>=MB:returnfmt.Sprintf("%.2fMB",b/MB)caseb>=KB:returnfmt.Sprintf("%.2fKB",b/KB)}returnfmt.Sprintf("%.2fB",b)}
表达式 YB 会打印出 1.00YB,而 ByteSize(1e13) 则会打印出 9.09。
在这里用 Sprintf 实现 ByteSize 的 String 方法很安全(不会无限递归),这倒不是因为类型转换,而是它以 %f 调用了 Sprintf,它并不是一种字符串格式:Sprintf 只会在它需要字符串时才调用 String 方法,而 %f 需要一个浮点数值。
fmt.Println(YB,ByteSize(1e13))//1.00YB9.09TB
Variables 变量
变量能像常量一样初始化,而且可以初始化为一个可在运行时得出结果的普通表达式。
The init function
最后,每个源文件都可以通过定义自己的无参数 init 函数来设置一些必要的状态。 (其实每个文件都可以拥有多个 init 函数。)而它的结束就意味着初始化结束: 只有该包中的所有变量声明都通过它们的初始化器求值后 init 才会被调用, 而包中的变量只有在所有已导入的包都被初始化后才会被求值。
除了那些不能被表示成声明的初始化外。init()函数会在每个包完成初始化后自动执行,并且执行优先级比main函数高。init 函数通常被用来:
对变量进行初始化
检查/修复程序的状态
注册
运行一次计算
funcinit(){ifuser==""{log.Fatal("$USERnotset")}ifhome==""{home="/home/"+user}ifgopath==""{gopath=home+"/go"}//gopathmaybeoverriddenby--gopathflagoncommandline.flag.StringVar(&gopath,"gopath",gopath,"overridedefaultGOPATH")}