我正在使用 Go 泛型在“请求-响应”风格的 goroutine 上编写一个类型安全的包装器,并偶然发现了一个编译错误:
type WorkerContext[In any, Out any] interface {
Process(In) (Out, bool)
Destroy()
}
type Worker[In any, Out any] struct {
context WorkerContext[In, Out]
in chan In
out chan struct{Out; bool}
}
这里,一个 goroutine 由一个
Worker
实例表示,在 goroutine 中执行以处理请求的用户代码由 WorkerContext
接口表示(该接口可能持有任意外部资源,例如一个子进程Goroutine 正在与之通信。
这里的想法是,
WorkerContext.Process()
函数返回处理请求的结果,以及指示外部资源是否可以接受更多请求的标志。这两个值都通过通道传输回请求线程。在那里,该标志用于决定是否将工作线程(及其关联的 goroutine 和上下文)放回到工作线程池中或销毁它(在下一个请求出现时创建一个新的)。
但是,我似乎无法声明这样的通道:
src/taskpool/taskpool.go:24:22: embedded field type cannot be a (pointer to a) type parameter
为什么会这样呢?这种限制感觉有点武断,我在这里想做的事情的惯用方法是什么?
此时实现此操作的唯一方法是使用显式字段名称:
type Worker[In any, Out any] struct {
context WorkerContext[In, Out]
in chan In
out chan struct {
Out Out
Ok bool
}
}
它还可以避免嵌入预先声明的标识符(例如
bool
)时出现意外。在您原来的示例中, Out
将被导出,而 bool
则不会。此处使用显式字段名称有助于阐明代码的用途。导出字段。
关于编译器为何拒绝这一点的最新讨论(尽管原始泛型提案暗示相反)可以在这里找到:cmd/compile:嵌入字段类型不能是(指向)类型参数。
我们初步得出结论,由于方法升级的问题,最初可能不允许嵌入类型参数。指向类型参数的指针也会出现同样的问题。但也许我们终究可以允许这种情况发生。
会重新考虑。
然后又十天后:
我们已经再次讨论过这一点,并且我们不会在 1.18 中更改这一点。理由:
- 关于这种嵌入字段的类型,还存在一些悬而未决的问题。也考虑一下这种情况:
func f[P constraint]() { type S struct{ P // <<< should this be permitted? } ... }
- 关于方法推广存在一些悬而未决的问题。
- 我们可以稍后添加它,但如果我们犯了错误,我们将无法删除该功能。
- 实施已受到限制;在代码冻结之前进行此类更改是有风险的。
仿制药提案仍然描述了这种可能性,这只是一个疏忽。最终,规范(正在进行中)将成为最终的指导文件。
结束。一旦我们获得了更多关于类型参数的经验,我们就可以重新考虑这个建议。