Go Data Structures: Interfaces by Russ Cox
Go's interfaces—static, checked at compile time, dynamic when asked
for
Go's interfaces let you use duck typing like you would in a purely
dynamic language like Python but still have the compiler catch obvious
mistakes like passing an int where an object with a Read method was
expected, or like calling the Read method with the wrong number of
arguments.
Interfaces aren't restricted to static checking, though. You can check
dynamically whether a particular interface value has an additional
method.
Interface Values
Languages with methods typically fall into one of two camps: prepare
tables for all the method calls statically (as in C++ and Java), or do
a method lookup at each call (as in Smalltalk and its many imitators,
JavaScript and Python included) and add fancy caching to make that
call efficient. Go sits halfway between the two: it has method tables
but computes them at run time. I don't know whether Go is the first
language to use this technique, but it's certainly not a common one.
Interface values are represented as a two-word pair giving a pointer
to information about the type stored in the interface and a pointer to
the associated data. Assigning b to an interface value of type
Stringer sets both words of the interface value.
The first word in the interface value points at what I call an
interface table or itable (pronounced i-table; in the runtime
sources). The itable begins with some metadata about the types
involved and then becomes a list of function pointers. Note that the
itable corresponds to the interface type, not the dynamic type.
The second word in the interface value points at the actual data, in
this case a copy of b.
Go's dynamic type conversions mean that it isn't reasonable for the
compiler or linker to precompute all possible itables: there are too
many (interface type, concrete type) pairs, and most won't be needed.
Instead, the compiler generates a type description structure for each
concrete type like Binary or int or func(map[int]string). Among other
metadata, the type description structure contains a list of the
methods implemented by that type. Similarly, the compiler generates a
(different) type description structure for each interface type like
Stringer; it too contains a method list. The interface runtime
computes the itable by looking for each method listed in the interface
type's method table in the concrete type's method table. The runtime
caches the itable after generating it, so that this correspondence
need only be computed once.
Method Lookup Performance
Smalltalk and the many dynamic systems that have followed it perform a
method lookup every time a method gets called. For speed, many
implementations use a simple one-entry cache at each call site, often
in the instruction stream itself. In a multithreaded program, these
caches must be managed carefully, since multiple threads could be at
the same call site simultaneously. Even once the races have been
avoided, the caches would end up being a source of memory contention.
Because Go has the hint of static typing to go along with the dynamic
method lookups, it can move the lookups back from the call sites to
the point when the value is stored in the interface.
How does Go interface dispatch work?
Method dispatch on an interface variable is the same as a vtable
dispatch.
The first time a concrete type hits an interface type, it builds a
hash table entry that points to a vtable. Second and subsequent
assignments of the same type will do a much cheaper hash lookup to
find the vtable. But the method dispatch itself is always
equivalent to a vtable lookup.
Spec: Interface types
For more details see: Go: What's the meaning of interface{}?
Here, two interesting use cases of interfaces in Go:
Why are interfaces needed in Golang?
The error type is an interface type: How to compare Golang error objects
Calculate Area of 4 different shapes: Circle, Square, Rectangle and Triangle:
Explain Type Assertions in Go
Here in Go you don't need do any thing special like Java keyword implements
for implementing an interface, in Go it is enough that your type just has that method with right signature.
Here is the code (try it on The Go Playground):
package main
import "fmt"
type Work struct {
Name string
}
func (t Work) String() string {
return "Stringer called."
}
func main() {
w := Work{"Hi"}
fmt.Println(w)
}
output:
Stringer called.
Spec: type Stringer, and see the source:
type Stringer interface {
String() string
}
Stringer is implemented by any value that has a String method, which
defines the “native” format for that value. The String method is used
to print values passed as an operand to any format that accepts a
string or to an unformatted printer such as Print.
Also see:
Why can't I assign a *Struct to an *Interface?
Meaning of a struct with embedded anonymous interface?
Embedded Interface
Golang: what's the point of interfaces when you have multiple inheritence