将函数分配给变量时,为什么编译器在以下情况下需要完美的函数签名匹配...

  • 变量的类型是一个函数,其参数或return是特定的接口,而
  • 被分配的函数需要一个不同的接口,但该接口嵌入了预期的接口。

  • 以这个例子为例...
  • 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值有什么不同?
  • 最佳答案

    简单地说,Fooer不是FooerBarer。两者都是接口类型,但是它们指向不同的itable。确保Fooer在itable中具有第一个方法是Foo() Fooer。在FooerBarer中,它的第一个方法可能是Bar() FooerBarer。因此,在运行时,方法查找将返回错误的方法。

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

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

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

    如果您确实需要执行以下操作,则有一个简单的答案。只需包装功能。

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

    10-08 12:37