Understanding Go Interfaces: A Deep Dive

Go is a statically-typed language, similar to Java and C#. However, one feature sets Go apart from these languages: the interface. To metaphorically describe it, an interface in Go acts like the shell of a clam: it wraps a value, and when you "open" the shell, you can access the value inside.

The iface Struct

In Go runtime, once you assign a value to a variable of interface type, the Go runtime wraps the value using the iface struct. Here's what the iface struct looks like:

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

This struct contains two main elements:

  • tab: A pointer to a type descriptor (itab)
  • data: A pointer to the actual value

You can check the iface implementation here.

The itab Struct

To further understand iface, let's explore the itab struct:

type itab struct {
    inter *interfacetype
    _type *_type
    hash  uint32 // copy of _type.hash. Used for type switches.
    _     [4]byte
    fun   [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}

The itab struct has:

  • inter: Information about the interface itself
  • _type: The actual type of the value, also known as the type descriptor

Additional information can be found in the itab implementation here.

Note: The length of fun appears to be 1, but this is actually a C-style memory optimization technique known as "flexible array member".

Example Code and Dynamic Dispatch

Let's look at a simple example:

package main

import "fmt"

type Shape interface {
    Area() float64
}

type Circle struct {
    radius float64
}

func (c Circle) Area() float64 {
    return 3.14 * c.radius * c.radius
}

func main() {
    var s Shape
    s = Circle{5.0}
    fmt.Println(s.Area())
}

In this code, a Circle type value is assigned to the interface-typed variable s. The Go runtime creates an iface struct, which includes an itab that has the Circle type information stored in its _type field. The fun field contains a pointer to an array of functions, which in this case is Area().

When s.Area() is called, the Go runtime dynamically dispatches the Area() function call through the interface s. It locates the pointer to the Area function and executes it.

profile
Quit talking, Begin doing

1개의 댓글

comment-user-thumbnail
2023년 8월 5일

글 재미있게 봤습니다.

답글 달기