这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战
Defer函数的参数(如果是一个方法,并且带有参数),在defer执行时计算,而不是在调用执行时计算。 一方面是担心在函数执行时变量会改变值,这意味着defer可以延迟多个函数执行。 这是一个愚蠢的例子
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
defer函数会按照 LIFO先进先出 顺序执行,因此代码将在函数返回时会打印 4 3 2 1 0。 一个更合理的例子是通过程序来跟踪函数执行。 我们可以编写几个简单的跟踪例程,如下所示:
func trace(s string) { fmt.Println("entering:", s) }
func untrace(s string) { fmt.Println("leaving:", s) }
// Use them like this:
func a() {
trace("a")
defer untrace("a")
// do something....
}
我们可以通过利用defer函数的参数,是在在defer执行时才计算这一事实,来做得更好。 跟踪例程可以为 untracing 例程设置参数。 这个例子:
func trace(s string) string {
fmt.Println("entering:", s)
return s
}
func un(s string) {
fmt.Println("leaving:", s)
}
func a() {
defer un(trace("a"))
fmt.Println("in a")
}
func b() {
defer un(trace("b"))
fmt.Println("in b")
a()
}
func main() {
b()
}
这样会打印
entering: b
in b
entering: a
in a
leaving: a
leaving: b
对于习惯于其他语言的块级「block-level」资源管理的程序员来说,defer 可能看起来很奇怪,但它最有趣和最强大的地方恰恰来自于它不是基于块而是基于函数的设计。 在关于 panic 和 recovery 的部分中,我们将看到其可能性的另一个例子。
Effective Go: 初始化(一)
虽然从表面上看它与 C 或 C++ 中的初始化没有太大区别,但 Go 中的初始化功能更强大。 可以在初始化期间构建复杂的结构,并且可以正确处理初始化对象之间,甚至不同包之间的排序问题。
常量
Go 中的常量就是——constant。 它们是在编译时创建的,即使在函数中定义为局部变量,并且只能是数字、字符、字符串或布尔值。 由于编译时限制,定义它们的表达式必须是可由编译器计算的常量表达式。 例如,1<<3 是一个常量表达式,而 math.Sin(math.Pi/4) 不是因为对 math.Sin 的函数调用需要在运行时发生。