Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Go generator函数,用于创建满足接口要求的值_Go - Fatal编程技术网

Go generator函数,用于创建满足接口要求的值

Go generator函数,用于创建满足接口要求的值,go,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 () (

我试图在一些代码中泛化超值类型。我需要做一大堆的值,然后对它们进行运算。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
,但情况似乎并非如此?

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

foo\u gen
定义为
func()(foo,error)
,因此第一个返回值的类型为
foo
,这是一个接口

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

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


编译器不允许将片
[]T
分配给接口{}类型的变量。

此错误的工程原因在@kostix的注释中给出。在类型级别上,这就是正在发生的情况

问题是go编译器无法将
func()(bar,error))
提升为
func()(Foo,error)
,因为一般来说go值不是

其结果是有几种方法可以解决这个问题

一种是更改函数的返回类型:

foo_gen := func () (Foo, error) {
  return bar{}, nil
}
bar_gen := func () (bar, error){
  return bar{}, nil
}
  
foo_gen := func() (Foo, error){
  foobar, err := bar_gen()
  return foobar, err
}
这是因为它能够轻松地将
bar{}
值提升为
Foo

如果我无法更改生成器功能(这也适用于通道等),另一种方法是将生成器包装到另一个功能中:

foo_gen := func () (Foo, error) {
  return bar{}, nil
}
bar_gen := func () (bar, error){
  return bar{}, nil
}
  
foo_gen := func() (Foo, error){
  foobar, err := bar_gen()
  return foobar, err
}

不,事实并非如此。签名必须逐字匹配。@Adrian套接字等如何工作?bc他们似乎导出了一个类型签名的联合?我不确定你们指的是什么,你们能告诉我显示这个的文档吗?这真的很有趣!我正在查找大约那个文档,但找不到它。这是否意味着这样的方法不可能工作?此外,这基本上是正确的答案,但实际解决问题的方法是更改函数的返回类型。(实际问题是它不能将
(unit->bar)
类型转换为
(unit->Foo)
类型,但编译器可以在生成器函数内部进行转换,即它是一个子类型问题)@Cjen1,Go不能有“子类型问题”,因为它没有子类型。Go有OOP,但Go实现的OOP方法不包含继承的概念。完全围棋没有继承权。这种错误的思维模式可能正是让你对手头的问题感到困惑的原因。解决这个问题的一种方法就是让具体的
foo_gen
(在
main
中定义)将
foo
作为其第一个返回值的类型。是的,我理解你关于go没有子类型的意思,但是ducktyping似乎只适用于原始值,而不适用于任何派生的值(函数、切片等),这意味着函数的返回类型不是协变的,这正是我想要说的。