Interface 为什么编译器需要如此严格的函数签名匹配?

Interface 为什么编译器需要如此严格的函数签名匹配?,interface,go,Interface,Go,当把一个函数赋给一个变量时,为什么编译器需要一个完美的函数签名匹配 变量的类型是一个函数,其参数或返回是一个特定的接口,并且 正在分配的函数需要不同的接口,但它是嵌入预期接口的接口 拿这个例子来说,其中 foore是一个接口 foorebarer是嵌入foorer接口的接口 *bar实现foorebarer //定义一个类型,该类型是返回Foore接口的函数 类型FMaker func()foore /*定义FMaker类型的值*/ //这是可行的,因为签名与FMaker类型匹配 va

当把一个函数赋给一个变量时,为什么编译器需要一个完美的函数签名匹配

  • 变量的类型是一个函数,其参数或返回是一个特定的接口,并且
  • 正在分配的函数需要不同的接口,但它是嵌入预期接口的接口
拿这个例子来说,其中

  • foore
    是一个接口
  • foorebarer
    是嵌入
    foorer
    接口的接口
  • *bar
    实现
    foorebarer

//定义一个类型,该类型是返回Foore接口的函数
类型FMaker func()foore
/*定义FMaker类型的值*/
//这是可行的,因为签名与FMaker类型匹配
var fmake FMaker=func()foore{
返回(&B){}
}
//这会导致错误,即使FooreBarer是Foorer
var fmake2 FMaker=func()footeBarer{
返回(&B){}
}
因此,我的问题不是关于替代解决方案,而是为什么编译器是以这种方式构建的

编译器似乎会看到,通过返回
footerber
,您将返回
footer
,并接受赋值

所以

  • 编译器这种严格行为的原因是什么
  • 解决了什么问题或避免了什么危险
  • 为什么这与编译器在赋值给
    foorer
    变量时接受
    foorerber
    值不同

    • 简单地说,食客不是食客。两者都是接口类型,但它们指向不同的itables。Fooer保证在itable be
      Foo()Fooer
      中有第一个方法。在FooteBarer中,它可能将
      Bar()FooteBarer
      作为其第一个方法。因此,在运行时,方法查找将返回错误的方法

      从FooerBarer到Fooer的任何转换都保证成功,因为FooerBarer始终具有Fooer所需的方法集。按照接口转换的工作方式,运行时首先查找它接收到的footerber的实际类型(例如bar),然后查找bar/footer对的itable并创建一个新的接口值

      在Go代码中,您可以显式或隐式地实现这一点。例如
      x:=foorer(myfoorebarer)
      。这将执行显式转换,并将新接口值放在x中。如果您有一个
      func(foore)
      类型的函数并传递了一个fooreBarer,那么转换将隐式进行。编译器将进行转换,并将结果分配给函数调用的参数

      在上面的例子中,您正试图将
      func()footerber
      分配给
      func()footer
      。在Go中,没有任何赋值具有自动转换。不能将double分配给int。甚至不能将time.Duration分配给int64,即使它们的基础类型相同。在这种情况下,需要包装函数,以便每次运行函数时都可以完成转换。不允许相同基础类型之间的转换是自动的,并且自动包装函数会有点不一致

      如果你真的需要这样做,有一个简单的答案。只需包装函数

      var fbmake = func() FooerBarer {
          return &bar{}
      }
      
      var fmake Fmaker = func() Fooer {
          return fbmake()
      }
      

      许多人认为“严格的接口是好的”。这就是编写强类型语言的全部意义,不是吗?我猜其他人都用Perl或Javascript编写代码;)@保罗4:我当然不反对这一点(我知道的还不够多,不同意;-)。但是关于接口,有“只要你能做X,Y和Z,我们就很酷”的感觉,我希望编译器能看到一个实现。谢谢你的回答。我正在努力正确理解你的第一段。你是说它与一个可预测的内存布局有关,它可以在运行时提供最快的查找速度?如果不是为了这样的好处,我不明白为什么它会有不同,因为编译器至少会保证返回的值至少具有
      foore
      所需的方法。是的,对于任何给定的接口,itable的顺序都是相同的。因此,如果需要Foo(),它是itable的第一个方法,并且与接口头中指向的数据有一定的偏移量。在FooreBarer中,第一个可能是Bar(),第二个可能是Foo()。好的,再次感谢。这在很大程度上对我来说是一个陌生的概念。您是否有机会推荐阅读Go或一般描述这些底层实现细节的书籍?或者,这是一种只能从阅读源代码中收集到的东西吗?“一个foore保证拥有itable be Foo()foore中的第一个方法。”保证???也许只有保证的废话。说明书中没有提到过“itable”,你说“在Go中,没有任务是自动转换的”。然而,情况并非如此。您可以将FooreBarer分配给Foorer,而无需(语法)强制转换。但是,不能将
      func()footerber
      分配给
      func()footer
      var fbmake = func() FooerBarer {
          return &bar{}
      }
      
      var fmake Fmaker = func() Fooer {
          return fbmake()
      }