go 建议:为其他类型的清理功能添加通用的lostcancel注解,

omtl5h9j  于 6个月前  发布在  Go
关注(0)|答案(8)|浏览(112)

提议详情

WithCancel函数返回一个"取消"函数,该函数必须在所有执行路径上调用。很容易忘记这样做,尤其是在早期返回错误路径中,导致上下文泄漏。这种模式非常常见:gopls代码库中有许多返回release函数的函数,这些函数会减少一个引用计数[ example ],我们因为没有遵循正确的规范而出现了许多bug。Kubernetes也有自己的许多这样的函数。搜索返回类型为func()的函数,通常命名为类似cleanupreleasestopcloseshutdown的函数,很容易找到更多这种模式的示例。

Go vet目前有一个lostcancel分析器,报告这类问题,并且将其泛化为处理除context.WithCancel之外的其他函数并不困难(我在周末实现了它)。但问题中最困难的部分是可靠地确定哪些函数是清理函数,哪些并不完全遵循规范[ example ]。名称并不是可靠的线索,而且许多函数都没有命名它们的结果变量。

WithCancel函数返回的实际类型是type CancelFunc func(),这是一个命名类型。这表明一种泛化的方法:允许模块定义自己的清理函数类型,类似于CancelFunc,然后使用我们计划设计和开发的注解机制将它们注册到分析器中。

以下可能是这样的样子:

${sketch}

这个提案显然次于注解提案,但一个立即的问题是:标准库是否应该提供一个标准的CancelFunc?长期观众可能会记得围绕一个类似的标准${e3f1x}注解进行的讨论,最终以“否”的决定告终。但是,当我们考虑将vet检查的模式泛化到标准库之外时,我们可能希望重新审视标准库是否应该为声明最重要的注解提供一个轻量级的包。这可能会带来一致性和减少样板的好处,并且还允许标准库使用注解(如果它们与分析器一起存在),否则它们将无法使用。

mi7gmzs6

mi7gmzs61#

是否有理由不通过魔术注解来实现这个功能?这将是我作为用户的第一React。例如,在返回闭包类型上使用 //go:vet nolostcancel

nkhmeac6

nkhmeac62#

也许 //vet:noLostCancelgo-vet:,所以它并不意味着它具有运行时支持?
从某种意义上说,似乎没有一个API可以获取这些特殊注解。如果不需要自己解析它们就好了。
NoCopy issue的链接不起作用。看起来是 #8005

sd2nnvve

sd2nnvve3#

有很多原因更喜欢注解而不是魔术注解。主要原因是它们由编译器和类型检查器检查,因此不能拼写错误,或者在迁移、重命名等过程中丢失。此外,它们是自文档的,因为你可以跳转到它们的定义,并使用你的IDE的引用查询进行枚举,使其易于找到使用示例。

uplii1fm

uplii1fm4#

我同意这个观点,但我对

func _() {
    lostcancel.Register(CancelFunc(nil))
}

并不是特别感兴趣。
如果你能写type A[T any] = T,你也可以写type MyCleanupFunc annotations.CleanupFunc[func()]。这会丢失一些类型检查,但vet也可以为这种情况提供错误信息。

ff29svar

ff29svar5#

如果你能写出 type A[T any] = T ,你就能写出 type MyCleanupFunc annotations.CleanupFunc[func()] 。这会丢失一些类型检查,但 vet 也可以为这种情况提供错误信息。
这是一个避免额外声明行的好方法,但我认为清理函数实际上并不需要改变其函数类型(如果这是可以表达的)。
我愿意接受关于如何编写一个空操作 Go 声明来表达“类型 T 是特殊的”的建议。

du7egjpx

du7egjpx6#

已经有了一个通用的约定,使用包级别的 var _ = 进行类型检查,例如 Impl 实现 Interface。将其整合到其中并不需要初始化函数会更好。理想情况下,它应该能够自动编译。

monwx1rj

monwx1rj7#

@earthboundkid 目前还不清楚你是否无法执行 var _ = ...。我认为提案没有明确说明 lostcancel.Register(...) 返回什么内容。如果它返回了某些内容,那么这是完全可行的。尽管 lostcancel.Register 不是一个运行时操作这一点可能不太清楚,但我对 func _() { ... } 对大多数 Go 程序员来说是否明显表示怀疑。

rlcwz9us

rlcwz9us8#

我误将下划线函数(underbar func)当作初始化函数(init func),例如。

相关问题