When assigning a function to a variable, why does the compiler require a perfect function signature match when...
Take this example where...
Fooer
is an interfaceFooerBarer
is an interface that embeds the Fooer
interface*bar
implements 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{}
}
So my question is not about an alternate solution, but rather why the compiler is built this way.
It would seem that the compiler would see that by returning a FooerBarer
, you are therefore returning a Fooer
, and would accept the assignment.
So...
FooerBarer
value in an assignment to a Fooer
variable?To put it simply, a Fooer is not a FooerBarer. Both are interface types, but they point to different itables. A Fooer is guaranteed to have the first method in the itable be Foo() Fooer
. In a FooerBarer, it may have Bar() FooerBarer
as its first method. So during runtime a method lookup would return the wrong method.
Any conversion from a FooerBarer to a Fooer is guaranteed to succeed because a FooerBarer always has the method set required for a Fooer. The way interface conversion works, the runtime first looks up the real type of the FooerBarer it has received (such as a bar) and then looks up the itable for the bar/Fooer pair and creates a new interface value.
In Go code, you can cause this to happen explicitly or implicitly. For example x := Fooer(myFooerBarer)
. This would do an explicit conversion and place the new interface value in x. If you had a function of type func(Fooer)
and passed a FooerBarer, then the conversion would happen implicitly. The compiler would do the conversion and assign the result to the parameter of the function call.
In your case above, you are attempting to assign a func() FooerBarer
to a func() Fooer
. In Go, no assignment has an automatic conversion. You can not assign a double to an int. You cannot even assign a time.Duration to an int64 even though their underlying types are identical. In this case, the function would need to be wrapped so that the conversion could be done each time the function was run. Not allowing conversions between the same underlying type to be automatic and automatically wrapping functions would be a bit inconsistent.
If you really need to do something like this, there is an easy answer. Just wrap the function.
var fbmake = func() FooerBarer {
return &bar{}
}
var fmake Fmaker = func() Fooer {
return fbmake()
}