Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

An interface in Go is an abstract type that defines a set of types. Prior to Go 1.18, an interface was defined strictly as a set of method signatures. With the introduction of generics, the Go specification expanded this definition: an interface defines a type set. This type set can be specified by a list of required methods, a union of concrete types, or a combination of both. Go interfaces are satisfied implicitly; a type implements an interface if it belongs to the interface’s type set, requiring no explicit declaration (such as an implements keyword).

Syntax and Declaration

An interface is declared using the type and interface keywords, followed by a block containing method signatures or type constraints.
// Interface defined by method signatures
type ReadWriter interface {
    Read(p []byte) (n int, err error)
    Write(p []byte) (n int, err error)
}

// Interface defined by a type set (used as a generic constraint)
type SignedInteger interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64
}

Implicit Implementation (Method Sets)

When an interface specifies methods, a concrete type implements that interface by possessing all the declared methods with exact matching signatures (parameters and return types). The Go compiler resolves this relationship statically at compile time.
type Greeter interface {
    Greet() string
}

type User struct {
    Name string
}

// User implicitly implements the Greeter interface
func (u User) Greet() string {
    return "Hello, " + u.Name
}

Interfaces as Type Constraints

As of Go 1.18, interfaces can embed concrete types using the union operator (|) and the approximation symbol (~) to restrict the types that can be used as type arguments in generic functions or structs.
type Number interface {
    int | float64
}

// T is constrained to the type set defined by Number
func Add[T Number](a, b T) T {
    return a + b
}

Internal Representation

At runtime, an interface value is represented as a two-word data structure (often referred to as a “fat pointer”). The exact internal structure depends on whether the interface specifies methods:
  1. Non-Empty Interfaces (iface): Used for interfaces with at least one method. It consists of:
    • itab (Interface Table): A pointer to a struct containing the dynamic type information and a dispatch table (array of function pointers) for the interface’s methods.
    • data: A pointer to the actual underlying concrete value.
  2. Empty Interfaces (eface): Used for interfaces with zero methods. Because no method dispatch is required, it consists of:
    • _type: A pointer directly to the runtime type information of the concrete value.
    • data: A pointer to the actual underlying concrete value.
Nil Interfaces: In Go, an interface is considered nil strictly if its dynamic type pointer (itab or _type) is nil. The data pointer is not evaluated during a nil comparison. Consequently, if an interface holds a nil pointer of a concrete type, the interface itself is non-nil because its type pointer (itab or _type) is populated.

The Empty Interface (any)

An interface that specifies zero methods is known as the empty interface, denoted as interface{}. Because every type belongs to the universal type set (implementing zero methods), an empty interface can hold a value of any concrete type. any is a built-in type alias for interface{}.
var val any // equivalent to var val interface{}

val = 42          // holds an int
val = "Go"        // holds a string
val = User{"Bob"} // holds a struct

Type Assertions

Because interfaces abstract away the concrete type, Go provides type assertions to extract the underlying value or check its specific type dynamically at runtime.
var i any = "hello"

// Safe type assertion
s, ok := i.(string)
if ok {
    // s is of type string
}

// Type switch for multiple possibilities
switch v := i.(type) {
case int:
    // v is an int
case string:
    // v is a string
default:
    // unknown type
}

Interface Embedding

Interfaces can be composed by embedding other interfaces. The resulting interface’s type set is the intersection of the type sets of the embedded interfaces. For method-based interfaces, this effectively creates a union of all method signatures.
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Closer interface {
    Close() error
}

// ReadCloser embeds Reader and Closer
type ReadCloser interface {
    Reader
    Closer
}
A concrete type must implement all methods from both Reader and Closer to satisfy the ReadCloser interface.
Master Go with Deep Grasping Methodology!Learn More