Go 生成器函数来创建满足接口的值

我试图概括一些代码中的值类型。我需要制作一大堆值,然后对它们进行操作。API 大致如下:


type Foo interface {

  A(string) error

}


func Run(foo_gen func () (Foo, error)) {

  ...

}

另一方面,我有:


type bar struct {

  ...

}


func (bar Bar) A (s string) {

  ...

}


func main() {

  foo_gen := func () (bar, error) {

  return bar{}, nil

  }

  mypackage.Run(foo_gen)

}

这失败并显示错误消息:


cannot use foo_gen (type func () (bar, error)) as type func() (mypackage.Foo, error) in argument to mypackage.Run

我还很陌生,所以我可能缺少一些基本的东西。我所期待的是编译器会尝试通过匹配方法类型(类似于记录子类型关系)显式bar转换为Foo,但情况似乎并非如此?


牧羊人nacy
浏览 88回答 2
2回答

HUWWW

解释可能令人惊讶,但实际上很简单。foo_gen被定义为func () (Foo, error),所以第一个返回值的类型Foo是 ,它是一个接口。任何接口类型的值都是struct具有两个指针的:指向该接口值中实际保存的具体值和指向(一些内部对象表示)它的类型。关于如何实现接口值的经典且非常好的(如果可能有点生疏)解释是this。您尝试匹配我们刚刚讨论的签名的实际函数返回一个类型的值Bar作为它的第一个返回值。该类型是一种struct类型,它与原始签名所期望的接口值无关。换句话说,为了让你的函数返回一个类型的值Bar满足期望接口类型值的签名,Foo编译器必须生成代码,将返回的值复制Bar到堆并从中合成一个值Foo。虽然可能是可行的,但这太含蓄了,否则可能会令人惊讶。由于几乎相同的原因,编译器不允许将切片分配给[]T类型变量。[]interface{}

叮当猫咪

在类型级别上,这就是正在发生的事情。问题是 go 编译器无法将 a 提升func () (bar, error))到 afunc () (Foo, error)因为通常 go 值不是covariant。这样做的结果是有几种方法可以解决这个问题。一种是改变函数的返回类型:foo_gen := func () (Foo, error) {  return bar{}, nil}这是有效的,因为它能够轻松地将bar{}值提升到一个Foo值。如果我无法更改生成器函数(这也适用于通道等),另一种方法是将生成器包装在另一个函数中:bar_gen := func () (bar, error){  return bar{}, nil}  foo_gen := func() (Foo, error){  foobar, err := bar_gen()  return foobar, err}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go