golang 的 defer 真是个好设计

29 天前
 afxcn

我们开发的时候使用了 sync.Pool ,所以需要考虑资源释放问题。

例如下面这样一段代码:

// bearerAuth is a function that performs bearer token authentication and authorization based on the provided access token and values.
func bearerAuth(c *web.Ctx, vals ...int64) error {

	accessToken := c.BearerToken()

	if accessToken == "" {
		return web.ErrUnauthorized
	}

	cat, err := proxy.GetAuthByAccessToken(accessToken)

	if err != nil {
		return err
	}

	c.Init(cat.UserID, cat.UserRight)

	if !utils.CheckVals(cat.UserRight, vals...) {
		cat.Release()
		return web.ErrForbidden
	}

	cat.Release()

	return nil
}

我们得在所有退出路径上调用 cat.Release(),有了 defer ,我们只需要这样就解决问题了

func bearerAuth(c *web.Ctx, vals ...int64) error {

	accessToken := c.BearerToken()

	if accessToken == "" {
		return web.ErrUnauthorized
	}

	cat, err := proxy.GetAuthByAccessToken(accessToken)

	if err != nil {
		return err
	}

	defer cat.Release()

	c.Init(cat.UserID, cat.UserRight)

	if !utils.CheckVals(cat.UserRight, vals...) {
		return web.ErrForbidden
	}

	return nil
}

如果是自己建对象,就更方便了:

user := model.CreateUser() defer user.Release()

8445 次点击
所在节点    Go 编程语言
81 条回复
kxct
29 天前
其他语言的析构函数了解下
guoziyan
29 天前
RAII 了解下
aababc
29 天前
感觉 python 的 with 更方便,也更容易理解
idealhs
29 天前
using
ResidualSoils
29 天前
C#什么时候能有这东西,using 太怪异了
fenfire
29 天前
想问下大佬,如果我用 defer 去关闭 redis 的连接,但是中间出异常了会不会导致到不了 defer 那一步,连接没关闭?
Chad0000
29 天前
@ResidualSoils #5
其实不加花括号的 Using 也算得上跟它差不多的写法
imherer
29 天前
@fenfire 直接在初始化链接成功后面紧接 defer 关闭就好了
masterclock
29 天前
defer 简直就是傻子吧
忽略过去所有的成果,硬是要来点 exotic 玩意儿,有提升没问题,问题是没提升啊
tsanie
29 天前
@ResidualSoils #5
defer 其实就相当于 try{}finally{},using 其实也是 try{}finally{}的糖
vimiix
29 天前
@fenfire 不会,即使后续逻辑 panic ,defer 也会被调用到
inhzus
29 天前
有些时候还是好用的。于是 C++ 有了 scope_exit ,C 23 也引入了 defer 关键字
weeei
29 天前
@inhzus C++ 的 scope_exit 还在 <experimental/scope>,进展太慢了。
seth19960929
29 天前
@kxct #1 两个概念不一样
dnfQzjPBXtWmML
29 天前
当初写 c 的时候总想着怎么搞个 c 版的 defer
Ericcccccccc
29 天前
看了下第二段代码,藏一个 defer 不是好设计

java 那种 try release 的写法我感觉挺好
hez2010
29 天前
那你是没有遇到 go 的 defer 在作用域上的坑,比如这段代码:
package main

import "fmt"

func main() {
for i := 0; i < 5; i++ {
defer foo()
fmt.Println(i)
}
}

func foo() {
fmt.Println("foo")
}
会输出
0 1 2 3 4 foo foo foo foo foo ,而不是 0 foo 1 foo 2 foo 3 foo 4 foo

而别的语言的无论是 RAII 还是 try-finally 还是 using ,作用域都是显然的,会在当前 scope 结束的时候被调用。
这也就导致你没法直接用 go 的 defer 来做退出锁之类的操作,因为全都被 go 给你放到函数最后去了。
Guaderxx
29 天前
@hez2010 想说点啥也不知道从哪说起,祝您今天开心
CEBBCAT
29 天前
@hez2010 #16

https://go.dev/ref/spec#Defer_statements 提到:
> A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns, either...

defer 是针对当前函数的,for 中的 defer 会在 for 所在的函数结束后逆序逐个执行,如果需要,可以使用匿名函数将目标 scope 明确包裹,只用两行:
https://go.dev/play/p/7YwquKzmFDd

我觉得这个不能完全算是“坑”吧
iOCZS
29 天前
Swift 、Python:嘿黑,我们也有

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1036972

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX