defer和go一样都是Go语言提供的关键字。defer用于资源的释放,会在函数返回之前进行调用。
但是defer也有你意想不到的坑,掉进去也找不明头绪的那种。
例子1
func f() (result int) {
defer func() {
result++
}()
return 0
}
例子2
func f() (r int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
例子3
func f() (r int) {
defer func(r int) {
r = r + 5
}(r)
return 1
}
请读者思考一下,这几条代码的结果会是什么呢?
然后在自己运行一下,看到结果的你会不会有点奇怪呢?
例1的正确答案不是0,例2的正确答案不是10,如果例3的正确答案不是6…
例1的正确答案是1,例2的正确答案是5,如果例3的正确答案是1…
(以上引用书籍《深入解析Go》,书籍已在github上发布
关注后评论区扣1,发中文版链接!!秒发哦!!)
Go语言中的return xxx语句本质上是三个步骤的非原子操作,就像电商仓库的订单处理流程:
装箱封口(赋值返回值):将货物放入快递盒,贴上地址标签(将xxx赋值给返回值变量)
最终质检(执行defer):检查包裹是否牢固、补写保修卡(执行所有注册的defer函数)
物流发车(真正返回):包裹离开仓库,进入运输流程(执行return指令跳回调用方)
关键陷阱:质检员(defer)可能在封箱后拆开包裹调整内容,导致最终收到的货物与装箱时不同。
场景1: 基础流程(无defer干扰)
func sendGift() string {
gift := "巧克力礼盒" // 仓库取出巧克力
return gift // 等价于:
// 1. 返回值 = "巧克力礼盒"(装箱)
// 2. 无defer(不质检)
// 3. return(发车)
}
// 客户收到:巧克力礼盒 ✅
场景2: defer篡改包裹内容
func sendGift() (result string) {
result = "巧克力礼盒" // 装箱
defer func() {
result = "石头" // 质检员调包
}()
return // 等价于:
// 1. 返回值 = "巧克力礼盒"
// 2. defer修改为"石头"
// 3. return
}
// 客户收到:石头
func f() (result int) {
defer func() {
result++
}()
return 0
}
可以改写成为:
func f() (result int) {
result = 0
result++ //defer执行
return
}
所以答案是1
func f() (r int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
可以改写:
func f() (r int) {
t := 5
r = t
t = t+5 //defer执行
return
}
所以答案是5
func f() (r int) {
defer func(r int) {
r = r + 5
}(r)
return 1
}
可以改写:
func f() (r int) {
r = 1
// defer 传入非指针所以会复制一个复制体进去,不会改变原值
defer func(r int) {
r = r + 5
}(r)
return
}
所以答案是1
func demo() int {
res := 10
defer func() { res += 5 }() // 修改的是局部变量,非返回值
return res // 装箱后defer无法触及
}
// 返回10(装箱后质检员无权开箱)
func demo() (res int) {
res = 10
defer func() { res += 5 }() // 直接操作快递盒内部
return // 装箱和质检可同步操作
}
// 返回15(质检员在发车前修改内容)
操作权限示意图:
匿名返回值:[封闭盒] → 封箱后移交质检(defer只能摸盒子表面)
命名返回值:[开口盒] → 封箱后仍可伸手调整内容(defer能穿透修改)
defer func(val int) {
// 使用val而非直接修改返回值
}(res)
file, _ := os.Open("data.txt")
defer file.Close() // 安全:关闭操作不会影响已读取的数据
制作不易
如果觉得本篇文章对你有帮助的话,麻烦点赞关注收藏啊~~收藏起来!
有机会的话,可以关注我的Golang专栏,免费的哦,每天更新一点跟Golang相关的知识哦,或者想要解答疑问的可以在评论区提出,可以解答哦~
Golang专栏链接: Golang那些事专栏