惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
L
LINUX DO - 最新话题
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Forbes - Security
Forbes - Security
博客园 - 司徒正美
Hugging Face - Blog
Hugging Face - Blog
W
WeLiveSecurity
Jina AI
Jina AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
N
News and Events Feed by Topic
V
V2EX
Stack Overflow Blog
Stack Overflow Blog
Engineering at Meta
Engineering at Meta
PCI Perspectives
PCI Perspectives
Martin Fowler
Martin Fowler
T
The Exploit Database - CXSecurity.com
F
Full Disclosure
WordPress大学
WordPress大学
S
Security Affairs
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
S
SegmentFault 最新的问题
P
Privacy International News Feed
IT之家
IT之家
M
MIT News - Artificial intelligence
G
GRAHAM CLULEY
Hacker News: Ask HN
Hacker News: Ask HN
D
DataBreaches.Net
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
C
Check Point Blog
美团技术团队
Security Latest
Security Latest
Cyberwarzone
Cyberwarzone
N
News and Events Feed by Topic
MyScale Blog
MyScale Blog
H
Help Net Security
宝玉的分享
宝玉的分享
The Hacker News
The Hacker News
The Last Watchdog
The Last Watchdog
The Cloudflare Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
爱范儿
爱范儿
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
I
Intezer
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
AI
AI
I
InfoQ
N
News | PayPal Newsroom
TaoSecurity Blog
TaoSecurity Blog

吴润写字的地方

从零构建 A 股 ETF 动量轮动系统:一个量化新手的完整指南 2025 年 8 月 Digest 2025 年 7 月 Digest 2025 年 6 月 Digest 2025 年 5 月 Digest 2025 年 4 月 Digest 清明桐庐行 2025 年 3 月 Digest 2024 年终总结 换车 崇明岛一日游 夜游徐汇滨江绿地 2023 年终总结 踏春之浦康休闲公园 香港澳门行 读「投资第一课」有感 皮皮玩球 Chrome 插件推荐 Go 并发之道:并发介绍 The Night Agent 我是如何使用 ChatGPT 的 2022 年终总结 在 Ubuntu 上搭建 MinIO 2021 年终总结 Golang 不可寻址的理解 恭王府 强制删除 k8s terminating 状态命名空间 PromQL 计算 CPU 使用率 PromQL 如何计算变化(率) Trickster 使用 奥森的向日葵 Go Modules - checksum mismatch 错误解决 CDN 使用初探 k8s 介绍 再探 798 解决 gin 路由冲突问题 wu.run 域名正式使用 GoLand 使用指南 macOS 软件推荐 2020 这一年 Python 循环列表删除元素的注意事项 Mac 下的定时任务工具:Launchctl Linux 服务器搭建 VNC 可视化桌面 Jenkins 个别 job 页面打开超时的解决方法 遇见 Aamir Khan Hexo 从 GitHub 到阿里云 『从未说出口的秘密』词云分析 768 的秋天 北戴河 Outing 飞盘记 三里屯夜景 爬虫图解:轮子哥关注了哪些人 2017-06-30 GitHub+Hexo 搭建个人网站详细教程 特朗普税改 如果有一天,苹果和微信只能选一个,你会选哪一个? About | 吴润写字的地方 Links | 吴润写字的地方 Memos | 吴润写字的地方
Golang 的 defer 关键字使用注意事项
2021-11-10 · via 吴润写字的地方

Golang 提供了 defer 关键字,用于函数退出前执行收尾工作,基本的使用方法就不再赘述了。总结了一些可能踩坑的地方。

package main

import "fmt"

func TestFunc() {
    defer func() {
        fmt.Println("A")
    }()

    defer func() {
        fmt.Println("B")
    }()

    defer func() {
        fmt.Println("C")
    }()
}

func main() {
	TestFunc()
}

执行结果是:

即 defer 执行先后顺序与代码顺序是相反的,是后进先出的顺序。

defer 与 return 执行顺序

package main

import "fmt"

func deferFunc() int {
    fmt.Println("defer func running")
    return 0
}

func returnFunc() int {
    fmt.Println("return func running")
    return 0
}

func TestFunc() int {
    defer deferFunc()
    return returnFunc()
}

func main() {
    TestFunc()
}

执行结果是:

return func running
defer func running

可以看到 return 语句先执行,defer 的函数后执行。

defer 影响返回值

既然 defer 执行的函数在 return 语句之后执行。那么 defer 是否可以改变函数返回值?来看下面的例子:

package main

import "fmt"

func TestFunc() (result int) {
    defer func() {
        result = 1024
    }()

    return 2048

}

func main() {
    fmt.Println(TestFunc())
}

执行结果是 1024。return 的是 2048,即 defer 确实可以改变函数返回值。再看个类似的例子:

package main

import "fmt"

func TestFunc() int {
    result := 1024

    defer func() {
        result = 2048
    }()

    return result
}

func main() {
    fmt.Println(TestFunc())
}

执行结果是 1024。这时 defer func 虽然更新了 result 的值,但并没有对函数返回值造成影响。原因是该函数的 int 类型返回值未命名(即匿名返回值),所以 defer func 无法修改该返回值,只是修改了 result 变量值。

defer 函数预计算参数值

package main

import (
	"fmt"
	"time"
)

func TestFunc() {
    startedAt := time.Now()
    defer fmt.Println(time.Since(startedAt))

    time.Sleep(time.Second)
}

func main() {
    TestFunc()
}

这个例子是计算函数的执行时间,sleep 1s 作为模拟,然后执行结果是:199ns,显然这不是预期的结果。期望的执行时间应该是大于 1s 的。那么问题出在哪里? 由于调用 defer 时会预计算函数参数值,即预计算 time.Since(startedAt) 这个参数的值,而不是 sleep 执行完结束后计算的。可以这样改符合预期:

package main

import (
    "fmt"
    "time"
)

func TestFunc() {
    startedAt := time.Now()
    defer func() {
        fmt.Println(time.Since(startedAt))
    }()

    time.Sleep(time.Second)
}

func main() {
    TestFunc()
}

defer 后面跟匿名函数即可,符合我们的期望。执行结果是:1.003861399s。

defer 与 panic

触发 defer 执行的时机有三个:

  • 包裹 defer 的函数遇到 return
  • 包裹 defer 的函数执行到最后
  • 发生 panic

来看下 defer 与 panic 相遇时会发生什么。

发生 panic 时在 defer 中不捕获异常

package main

import "fmt"

func TestFunc() {
    defer func() {
        fmt.Println("A")
    }()

    defer func() {
        fmt.Println("B")
    }()

    panic("panic occurred")
}

func main() {
    TestFunc()
}

执行结果:

B
A
panic: panic occurred
... // panic 堆栈信息

发生 panic 时在 defer 中捕获异常

package main

import "fmt"

func TestFunc() {
    defer func() {
        fmt.Println("A")
    }()

    defer func() {
        fmt.Println("B")
        if err := recover(); err != nil {
            fmt.Println("catch panic")
        }
    }()

    panic("panic occurred")
}

func main() {
    TestFunc()
}

执行结果是:

这个例子执行结果没什么可说的,发生 panic,可以在 defer 函数中捕获处理。

defer 中发生 panic

package main

import "fmt"

func TestFunc() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Printf("catch %s panic\n", err)
        }
    }()

    defer func() {
        panic("inner")
    }()

    panic("outer")
}

func main() {
    TestFunc()
}

执行结果是:catch inner panic。即外层发生 panic(“outer”),触发 defer 执行,执行 defer 中又发生 panic(“inner”),最后一个 defer 中捕获 panic,捕获到的是最后一个 panic。

另外,尽量不要使用 panic,仅在发生不可恢复的错误或程序启动时发生意外才使用 panic。