Fundamentals 39 min read

Deep Dive into Go Interfaces: Duck Typing, Receivers, Interface Internals, and Polymorphism

The article thoroughly examines Go’s interface system, explaining static duck typing, the differences between value and pointer receivers, the internal iface/eface structures, how interfaces are constructed and converted, compiler implementation checks, type assertions, runtime polymorphism, and contrasts these mechanisms with C++ abstract‑class interfaces.

Didi Tech
Didi Tech
Didi Tech
Deep Dive into Go Interfaces: Duck Typing, Receivers, Interface Internals, and Polymorphism

This article provides a comprehensive exploration of Go interfaces, covering duck typing, the distinction between value and pointer receivers, the internal representation of interfaces (iface and eface), interface construction, compiler checks, type conversion, assertions, polymorphism, and a comparison with C++ interfaces.

1. Go and Duck Typing

The article starts by quoting the classic duck‑typing definition and explains that Go, although a static language, supports duck typing through its interface mechanism. An example in Python is shown:

def hello_world(coder):
    coder.say_hello()

In Go, the same idea is expressed by defining an interface and implementing the required method:

type IGreeting interface {
    sayHello()
}

func sayHello(i IGreeting) {
    i.sayHello()
}

Two concrete types implement the method:

type Go struct{}
func (g Go) sayHello() {
    fmt.Println("Hi, I am GO!")
}

type PHP struct{}
func (p PHP) sayHello() {
    fmt.Println("Hi, I am PHP!")
}

Calling sayHello(golang) and sayHello(php) demonstrates Go's static duck typing.

2. Value vs. Pointer Receivers

The article explains that methods can have either a value receiver or a pointer receiver. It shows that both value and pointer types can call either kind of method, but the compiler treats them differently. Example code:

type Person struct {
    age int
}

func (p Person) howOld() int {
    return p.age
}

func (p *Person) growUp() {
    p.age += 1
}

func main() {
    qcrao := Person{age: 18}
    fmt.Println(qcrao.howOld()) // 18
    qcrao.growUp()               // modifies original
    fmt.Println(qcrao.howOld()) // 19

    stefno := &Person{age: 100}
    fmt.Println(stefno.howOld()) // 100
    stefno.growUp()
    fmt.Println(stefno.howOld()) // 101
}

A table summarises the behavior of value and pointer receivers for both value and pointer callers.

3. iface vs. eface

The internal structures are presented:

type iface struct {
    tab  *itab
    data unsafe.Pointer
}

type eface struct {
    _type *_type
    data  unsafe.Pointer
}

Details of itab (interface table) and _type are shown, including fields such as inter , _type , hash , and the function pointer slice fun . The article includes assembly‑level diagrams and the layout of these structures.

4. Interface Construction

The process of converting a concrete value to an interface value is illustrated with the compiler‑generated function runtime.convT2I64 . The source of that function is reproduced:

func convT2I64(tab *itab, elem unsafe.Pointer) (i iface) {
    t := tab._type
    var x unsafe.Pointer
    if *(*uint64)(elem) == 0 {
        x = unsafe.Pointer(&zeroVal[0])
    } else {
        x = mallocgc(8, t, false)
        *(*uint64)(x) = *(*uint64)(elem)
    }
    i.tab = tab
    i.data = x
    return
}

The article walks through the generated assembly for a simple program that assigns a Student value to a Person interface, showing how the itab address is loaded and how runtime.convT2I64 is called.

5. Compiler Interface Implementation Checks

It demonstrates the common pattern used to verify at compile time that a type implements an interface:

var _ io.Writer = (*myWriter)(nil)
var _ io.Writer = myWriter{}

If the methods are missing, the compiler emits an error indicating the missing implementation.

6. Interface Conversion (convI2I)

When converting one interface to another, the runtime uses runtime.convI2I :

func convI2I(inter *interfacetype, i iface) (r iface) {
    tab := i.tab
    if tab == nil {
        return
    }
    if tab.inter == inter {
        r.tab = tab
        r.data = i.data
        return
    }
    r.tab = getitab(inter, tab._type, false)
    r.data = i.data
    return
}

The helper getitab looks up or creates an itab in a global hash table, ensuring that the conversion is fast after the first lookup.

7. Type Assertions and Switches

The article explains safe and unsafe type assertions, showing panic‑inducing and safe forms:

var i interface{} = new(Student)
// unsafe, will panic
s := i.(Student)

// safe
s, ok := i.(Student)
if ok {
    fmt.Println(s)
}

It also demonstrates a type‑switch that distinguishes nil , Student , and *Student values, printing the addresses of the variables at each stage.

8. Polymorphism via Interfaces

Using a Person interface with methods job() and growUp() , the article shows how both Student and Programmer types satisfy the interface, enabling polymorphic calls:

type Person interface {
    job()
    growUp()
}

type Student struct { age int }
func (p Student) job() { fmt.Println("I am a student.") }
func (p *Student) growUp() { p.age++ }

type Programmer struct { age int }
func (p Programmer) job() { fmt.Println("I am a programmer.") }
func (p Programmer) growUp() { p.age += 10 }

func whatJob(p Person) { p.job() }
func growUp(p Person) { p.growUp() }

Running the program prints the appropriate messages and updated ages, illustrating runtime polymorphism.

9. Comparison with C++ Interfaces

The article contrasts Go's non‑intrusive interface implementation with C++'s abstract‑class (pure virtual) approach. It notes that Go's itab and function pointers are generated at runtime, whereas C++ v‑tables are created at compile time.

10. References

A list of reference links and author information is provided at the end of the article.

GoRuntimeduck typinginterface{}method receiverpolymorphismType Assertion
Didi Tech
Written by

Didi Tech

Official Didi technology account

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.