为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
牧羊人nacy 2022-06-27 15:41:40
我试图概括一些代码中的值类型。我需要制作一大堆值,然后对它们进行操作。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,但情况似乎并非如此?
查看完整描述

2 回答

?
HUWWW

TA贡献1874条经验 获得超12个赞

解释可能令人惊讶,但实际上很简单。

foo_gen被定义为func () (Foo, error),所以第一个返回值的类型Foo是 ,它是一个接口。

任何接口类型的值都是struct具有两个指针的:指向该接口值中实际保存的具体值和指向(一些内部对象表示)它的类型。关于如何实现接口值的经典且非常好的(如果可能有点生疏)解释是this

您尝试匹配我们刚刚讨论的签名的实际函数返回一个类型的值Bar作为它的第一个返回值。该类型是一种struct类型,它与原始签名所期望的接口值无关。
换句话说,为了让你的函数返回一个类型的值Bar满足期望接口类型值的签名,Foo编译器必须生成代码,将返回的值复制Bar到堆并从中合成一个值Foo。虽然可能是可行的,但这太含蓄了,否则可能会令人惊讶。

由于几乎相同的原因,编译器不允许将切片分配给[]T类型变量。[]interface{}


查看完整回答
反对 回复 2022-06-27
?
叮当猫咪

TA贡献1776条经验 获得超12个赞

在类型级别上,这就是正在发生的事情。


问题是 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

}


查看完整回答
反对 回复 2022-06-27
  • 2 回答
  • 0 关注
  • 87 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信