为什么在 Golang 中返回一个函数?

huangapple go评论94阅读模式
英文:

Why return a func in Golang

问题

我最近在研究Golang的上下文(context),发现WithCancel()的实现方式很有趣。

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
	if parent == nil {
		panic("cannot create context from nil parent")
	}
	c := newCancelCtx(parent)
	propagateCancel(parent, &c)
	return &c, func() { c.cancel(true, Canceled) }
}

WithCancel()返回一个ctx和一个用于取消相同上下文的函数。为什么不像下面这样,将.Cancel()作为类型本身的一个函数引入呢?

func (c *cancelCtx) Cancel() {
  c.cancel(true, Canceled)
} 

我理解使用函数作为返回类型可以根据运行时条件运行不同的函数,但这里没有动态性 - 它始终是相同的函数。这只是因为函数式编程范式吗?

参考:https://cs.opensource.google/go/go/+/master:src/context/context.go;l=232-239?q=context&ss=go%2Fgo

英文:

I was looking into golang contexts recently, and found that the WithCancel() is implemented in an interesting way.

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
	if parent == nil {
		panic("cannot create context from nil parent")
	}
	c := newCancelCtx(parent)
	propagateCancel(parent, &c)
	return &c, func() { c.cancel(true, Canceled) }
}

WithCancel() returns a ctx, and also a func to cancel the very same context. Why is this done rather than introducing the .Cancel() as a func of the type itself, like

func (c *cancelCtx) Cancel() {
  c.cancel(true, Canceled)
} 

I understand using a func return type allows you to run a different func depending on runtime conditions, but there's no dynamic here - it's always the same func. Is this just because for the functional paradigm?

Reference: https://cs.opensource.google/go/go/+/master:src/context/context.go;l=232-239?q=context&ss=go%2Fgo

答案1

得分: 4

并非所有的上下文都可以取消。你可以认为对于那些不能取消的上下文,Cancel() 方法可以是一个空操作。

但是这样的话,每当你使用 context.Context 时都必须调用 Cancel(),因为你不知道(也无法知道)它是否真的需要取消。这在很多情况下都是不必要的,会使代码变慢(取消函数通常是延迟调用的),并且会使代码变得臃肿。

此外,取消上下文的权力只属于其创建者。创建者可以选择通过传递/共享取消函数来分享这个责任,但如果不这样做,仅仅共享上下文本身是不允许(也不应该允许)取消它的。如果 Cancel()context.Context 的一部分,这个限制就无法强制执行了。有关详细信息,请参阅 https://stackoverflow.com/questions/66679577/cancel-context-from-child/66679667#66679667

接口,尤其是那些被广泛使用的接口,应该是小而精简的,不包含所有的、很少有用的东西。

英文:

Not all contexts are cancel-able. You could argue that for those that aren't, the Cancel() method could be a no-op.

But then you would always have to call Cancel() whenever you work with context.Context because you don't (can't) know whether it truly needs cancelling. This would be unnecessary in a lot of cases, would make code slower (cancel functions are usually called deferred) and would bloat the code.

Also, the power of cancelling a context is for its creator only. The creator may choose to share this responsibility by passing / sharing the cancel function, but if doesn't, sharing the context alone does not allow (should not allow) cancelling it. If Cancel() would be part of context.Context, this restriction could not be enforced. For details, see https://stackoverflow.com/questions/66679577/cancel-context-from-child/66679667#66679667.

Interfaces–especially those widely used–should be small and a minimum, not containing all, rarely useful things.

huangapple
  • 本文由 发表于 2021年12月1日 20:40:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/70184384.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定