这取决于方法接收器和变量的类型。
简短回答:如果您使用 database/sql 包,您的延迟 Rows.Close() 方法将正确关闭您的两个 Rows 实例,因为 Rows.Close() 具有 指针 接收器 和 因为DB.Query() 返回一个指针(所以rows 是一个指针)。请参阅下面的推理和解释。
为避免混淆,我建议使用不同的变量,这将清楚您想要什么以及将要关闭什么:
rows := Query(`SELECT FROM whatever`)
defer rows.Close()
// ...
rows2 := Query(`SELECT FROM whatever`)
defer rows2.Close()
我想指出一个重要的事实,即来自延迟函数及其被立即评估的参数,这在 Effective Go 博客文章和 Language Spec: Deferred statements 中也有说明:
每次执行“defer”语句时,调用的函数值和参数都会像往常一样进行评估并重新保存,但不会调用实际函数。相反,延迟函数会在周围函数返回之前立即调用,与它们被延迟的顺序相反。
如果变量不是指针:调用延迟方法时会观察到不同的结果,具体取决于方法是否有指针接收器。
如果变量是指针,您将始终看到“想要的”结果。
看这个例子:
type X struct {
S string
}
func (x X) Close() {
fmt.Println("Value-Closing", x.S)
}
func (x *X) CloseP() {
fmt.Println("Pointer-Closing", x.S)
}
func main() {
x := X{"Value-X First"}
defer x.Close()
x = X{"Value-X Second"}
defer x.Close()
x2 := X{"Value-X2 First"}
defer x2.CloseP()
x2 = X{"Value-X2 Second"}
defer x2.CloseP()
xp := &X{"Pointer-X First"}
defer xp.Close()
xp = &X{"Pointer-X Second"}
defer xp.Close()
xp2 := &X{"Pointer-X2 First"}
defer xp2.CloseP()
xp2 = &X{"Pointer-X2 Second"}
defer xp2.CloseP()
}
输出:
Pointer-Closing Pointer-X2 Second
Pointer-Closing Pointer-X2 First
Value-Closing Pointer-X Second
Value-Closing Pointer-X First
Pointer-Closing Value-X2 Second
Pointer-Closing Value-X2 Second
Value-Closing Value-X Second
Value-Closing Value-X First
在Go Playground 上试试。
使用指针变量,结果总是好的(如预期的那样)。
使用非指针变量和指针接收器我们看到相同的打印结果(最新),但如果我们有值接收器,它会打印 2 个不同的结果。
非指针变量说明:
如前所述,当defer 执行时,会评估包含接收器的延迟函数。如果是指针接收器,它将是局部变量的地址。因此,当您为其分配一个新值并调用另一个defer 时,指针接收器将再次成为局部变量的相同地址(只是指向的值不同)。所以稍后在执行函数时,两者都会使用相同的地址两次,但 pointed 的值将是相同的,即稍后分配的那个。
如果是值接收器,接收器是一个副本,它是在defer 执行时生成的,所以如果你为变量分配一个新值并调用另一个defer,另一个副本将与之前的不同。