Skip to main content
Go supports embedding of structs and interfaces to express seamless composition of types. This provides a way to reuse code and build complex types from simpler ones, similar to inheritance in other languages but through composition.
Struct embedding is different from the //go:embed directive (introduced in Go 1.16+) which embeds files and folders into binaries. See the embed directive page for that feature.

Basic embedding

package main

import "fmt"

type base struct {
    num int
}

func (b base) describe() string {
    return fmt.Sprintf("base with num=%v", b.num)
}

// A container embeds a base. An embedding looks like a field without a name.
type container struct {
    base
    str string
}

func main() {
    // When creating structs with literals, we have to initialize
    // the embedding explicitly; the embedded type serves as the field name.
    co := container{
        base: base{
            num: 1,
        },
        str: "some name",
    }

    // We can access the base's fields directly on co, e.g. co.num.
    fmt.Printf("co={num: %v, str: %v}\n", co.num, co.str)

    // Alternatively, we can spell out the full path using
    // the embedded type name.
    fmt.Println("also num:", co.base.num)

    // Since container embeds base, the methods of base also
    // become methods of container. Here we invoke a method that
    // was embedded from base directly on co.
    fmt.Println("describe:", co.describe())
}
Output:
co={num: 1, str: some name}
also num: 1
describe: base with num=1

How embedding works

1

Field promotion

Fields from the embedded struct are “promoted” to the outer struct. You can access them directly:
co.num        // Promoted from base.num
co.base.num   // Explicit path also works
2

Method promotion

Methods from the embedded struct also become methods of the outer struct:
co.describe()  // Calls base.describe()
3

Interface satisfaction

If the embedded type implements an interface, the outer type automatically implements it too:
type describer interface {
    describe() string
}

// container implements describer because it embeds base
var d describer = co

Embedding vs composition

type container struct {
    base  // Embedded (no field name)
    str string
}

c := container{base: base{num: 1}, str: "hello"}
c.num  // Direct access
Provides field and method promotion.

Interface embedding

Interfaces can also be embedded to compose larger interfaces:
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

// ReadWriter embeds both Reader and Writer
type ReadWriter interface {
    Reader
    Writer
}

// Any type implementing both Read and Write satisfies ReadWriter

Common use cases

Adding behavior

Extend a base type with additional fields and methods

Interface composition

Build complex interfaces from smaller ones

Mixins

Share common functionality across types

Decorator pattern

Wrap types to add functionality

Example: Multiple embeddings

type logger struct {
    prefix string
}

func (l logger) log(msg string) {
    fmt.Printf("[%s] %s\n", l.prefix, msg)
}

type validator struct {
    strict bool
}

func (v validator) validate() bool {
    return v.strict
}

// Service embeds both logger and validator
type service struct {
    logger
    validator
    name string
}

func main() {
    s := service{
        logger:    logger{prefix: "INFO"},
        validator: validator{strict: true},
        name:      "api",
    }
    
    s.log("Starting service")  // From logger
    fmt.Println(s.validate())  // From validator
}

Naming conflicts

If multiple embedded types have fields or methods with the same name, you must use the explicit path:
type A struct { value int }
type B struct { value int }
type C struct {
    A
    B
}

c := C{}
// c.value        // ✗ Ambiguous! Compiler error
c.A.value         // ✓ Explicit
c.B.value         // ✓ Explicit

Best practices

Go’s embedding provides a form of composition. Favor small, focused types that you compose together rather than large hierarchies.
Embedding is particularly useful when you want to satisfy an interface by reusing an existing implementation.
While promoted fields are convenient, use explicit paths (co.base.num) when it improves clarity.
Make it clear in comments when a type embeds another and why.

Structs

Learn about struct basics

Methods

Define methods on types

Interfaces

Interface composition

Build docs developers (and LLMs) love