提议详情
WithCancel函数返回一个"取消"函数,该函数必须在所有执行路径上调用。很容易忘记这样做,尤其是在早期返回错误路径中,导致上下文泄漏。这种模式非常常见:gopls代码库中有许多返回release
函数的函数,这些函数会减少一个引用计数[ example ],我们因为没有遵循正确的规范而出现了许多bug。Kubernetes也有自己的许多这样的函数。搜索返回类型为func()
的函数,通常命名为类似cleanup
、release
、stop
、close
或shutdown
的函数,很容易找到更多这种模式的示例。
Go vet目前有一个lostcancel分析器,报告这类问题,并且将其泛化为处理除context.WithCancel
之外的其他函数并不困难(我在周末实现了它)。但问题中最困难的部分是可靠地确定哪些函数是清理函数,哪些并不完全遵循规范[ example ]。名称并不是可靠的线索,而且许多函数都没有命名它们的结果变量。
WithCancel函数返回的实际类型是type CancelFunc func()
,这是一个命名类型。这表明一种泛化的方法:允许模块定义自己的清理函数类型,类似于CancelFunc,然后使用我们计划设计和开发的注解机制将它们注册到分析器中。
以下可能是这样的样子:
${sketch}
这个提案显然次于注解提案,但一个立即的问题是:标准库是否应该提供一个标准的CancelFunc?长期观众可能会记得围绕一个类似的标准${e3f1x}注解进行的讨论,最终以“否”的决定告终。但是,当我们考虑将vet检查的模式泛化到标准库之外时,我们可能希望重新审视标准库是否应该为声明最重要的注解提供一个轻量级的包。这可能会带来一致性和减少样板的好处,并且还允许标准库使用注解(如果它们与分析器一起存在),否则它们将无法使用。
8条答案
按热度按时间mi7gmzs61#
是否有理由不通过魔术注解来实现这个功能?这将是我作为用户的第一React。例如,在返回闭包类型上使用
//go:vet nolostcancel
。nkhmeac62#
也许
//vet:noLostCancel
或go-vet:
,所以它并不意味着它具有运行时支持?从某种意义上说,似乎没有一个API可以获取这些特殊注解。如果不需要自己解析它们就好了。
NoCopy issue的链接不起作用。看起来是 #8005
sd2nnvve3#
有很多原因更喜欢注解而不是魔术注解。主要原因是它们由编译器和类型检查器检查,因此不能拼写错误,或者在迁移、重命名等过程中丢失。此外,它们是自文档的,因为你可以跳转到它们的定义,并使用你的IDE的引用查询进行枚举,使其易于找到使用示例。
uplii1fm4#
我同意这个观点,但我对
并不是特别感兴趣。
如果你能写
type A[T any] = T
,你也可以写type MyCleanupFunc annotations.CleanupFunc[func()]
。这会丢失一些类型检查,但vet也可以为这种情况提供错误信息。ff29svar5#
如果你能写出
type A[T any] = T
,你就能写出type MyCleanupFunc annotations.CleanupFunc[func()]
。这会丢失一些类型检查,但 vet 也可以为这种情况提供错误信息。这是一个避免额外声明行的好方法,但我认为清理函数实际上并不需要改变其函数类型(如果这是可以表达的)。
我愿意接受关于如何编写一个空操作 Go 声明来表达“类型 T 是特殊的”的建议。
du7egjpx6#
已经有了一个通用的约定,使用包级别的
var _ =
进行类型检查,例如 Impl 实现 Interface。将其整合到其中并不需要初始化函数会更好。理想情况下,它应该能够自动编译。monwx1rj7#
@earthboundkid 目前还不清楚你是否无法执行
var _ = ...
。我认为提案没有明确说明lostcancel.Register(...)
返回什么内容。如果它返回了某些内容,那么这是完全可行的。尽管lostcancel.Register
不是一个运行时操作这一点可能不太清楚,但我对func _() { ... }
对大多数 Go 程序员来说是否明显表示怀疑。rlcwz9us8#
我误将下划线函数(underbar func)当作初始化函数(init func),例如。