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

时间:2012-10-27 22:54:22

标签: interface go

将函数赋值给变量时,为什么编译器需要完美的函数签名匹配...

  • 变量的类型是一个函数,其参数或返回值是特定的接口,
  • 分配的功能需要不同的接口,但是是嵌入预期接口的接口。

将这个例子放在......

  • Fooer是一个界面
  • FooerBarer是嵌入Fooer接口
  • 的界面
  • *bar实施FooerBarer

http://play.golang.org/p/8NyTipiQak

    // Define a type that is a function that returns a Fooer interface
type FMaker func() Fooer

/* Define values of the FMaker type */

    // This works, because the signature matches the FMaker type
var fmake FMaker = func() Fooer {
    return &bar{}
}

    // This causes an error even though a FooerBarer is a Fooer
var fmake2 FMaker = func() FooerBarer {
    return &bar{}
}

所以我的问题不是替代解决方案,而是编译器以这种方式构建的原因。

似乎编译器会通过返回FooerBarer来看到,因此您返回Fooer,并接受分配。

因此...

  • 编译器严格行为的原因是什么?
  • 正在解决什么问题或避免危险?
  • 为什么这与编译器在FooerBarer变量的赋值中接受Fooer值有什么不同?

1 个答案:

答案 0 :(得分:3)

简而言之,Fooer不是FooerBarer。两者都是接口类型,但它们指向不同的itables。保证Fooer的第一种方法是Foo() Fooer。在FooerBarer中,它可能有Bar() FooerBarer作为其第一种方法。因此,在运行时,方法查找将返回错误的方法。

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

在Go代码中,您可以明确或隐式地使其发生。例如x := Fooer(myFooerBarer)。这将进行显式转换并将新接口值放在x中。如果您具有类型func(Fooer)的函数并传递了FooerBarer,则转换将隐式发生。编译器将执行转换并将结果分配给函数调用的参数。

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

如果你真的需要做这样的事情,那么答案很简单。只需包装该功能。

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

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