Imagine that we have the following code:
type A struct {}
func (a A) Foo() {
fmt.Println("Foo called")
}
type I interface {
Foo()
}
I can now create a variable of type A
and call this method:
a := A{}
a.Foo()
The compiler knows the static type of the variable, so knows that the method call refers to the A.Foo
method. So it can compile the above code to use a direct call to A.Foo
, which will be as fast as a normal function call.
If instead we use a variable of type I
, things are different:
var i I = A{}
i.Foo()
The variable i
can hold any type that has a Foo
method. In this particular case it is holding an A
value, but won't necessarily know this at compile time. So instead the compiler generates code to check the dynamic type of i
, look up the associated Foo
method and finally call that method. This form of dispatch is slower than the first, but has the benefit the code will work for any type implementing the interface.
This is similar to C++'s distinction between virtual and non-virtual methods, except rather than the type of dispatch being fixed for a method at its definition, it depends on the type of variable you use in Go.