Menu

Golang Embed Struct: Embedding and Promoted Methods

Embedding puts one type inside another without a field name, so its fields and methods are promoted to the outer type. Learn how promotion works, embedding interfaces, name conflicts, and why embedding is not inheritance.

This page includes runnable editors - edit, run, and see output instantly.

Embedding in one example

A field with a type but no name is an embedded field. The embedded type's fields and methods become reachable directly on the outer type.

Output:

Ana
Hi, I'm Ana
ana@example.com
{User:{Name:Ana Email:ana@example.com} Level:2}

The embedded field's name is its type name, User. That is how you refer to it in literals (User: User{...}) and when you need the inner value itself (a.User). A promoted field cannot be set by its short name in a literal: Admin{Name: "Ana"} fails with unknown field Name in struct literal of type Admin.

Promotion adds the embedded type's methods to the outer type's method set. So the outer type satisfies every interface the inner type does.

The method set rules follow the embedded type. Embedding T promotes T's value-receiver methods to both the outer value and the outer pointer, but *T's pointer-receiver methods only to the outer pointer. Embedding *T promotes both sets to both. If in doubt, embed the pointer, or use the outer type through a pointer.

Embedding a pointer has a cost: the pointer can be nil. Service{} with no Logger compiles, and svc.Log(...) then panics.

Embedding is not inheritance

Embedding looks like subclassing, but three things behave differently from Java or Python.

The outer type is not the inner type. An Admin is not a User. A function that takes a User will not accept an Admin; pass a.User.

There is no virtual dispatch. A promoted method runs on the embedded value, and it knows nothing about the outer type. If it calls another method, it calls its own type's version, even when the outer type defines one with the same name.

Output:

woof
The animal says ...

In a class-based language, Speak would print "woof". In Go, d.Speak() is shorthand for d.Animal.Speak(), and the receiver of that call is the Animal. When you want behavior that varies by type, use an interface: pass a Sounder into the code that needs it.

The inner type does not know it is embedded. There is no super. To extend a promoted method, define a method with the same name on the outer type and call the inner one explicitly:

func (a Admin) Greet() string {
	return a.User.Greet() + " (admin)"
}

Name conflicts and shadowing

A field or method on the outer type shadows a promoted one with the same name, as Dog.Sound did above. The shallower name wins.

When two embedded types at the same depth promote the same name, that name becomes ambiguous. The program still compiles as long as nothing uses the ambiguous name:

Resolve the conflict by using the full path, or by defining ID on the outer type.

Embedding interfaces

Interfaces can embed other interfaces. The standard library builds larger interfaces this way:

type ReadWriter interface {
	Reader
	Writer
}

A struct can also embed an interface. The struct then satisfies that interface through whatever value you store in the field, and you override only the methods you care about. This is the decorator pattern with almost no code:

The explicit c.Reader.Read(p) is required. Writing c.Read(p) inside Read would call itself forever.

Embedding an interface in a test fake is a common shortcut: embed the big interface, implement the one method the test needs, and leave the rest. Any unimplemented method panics with a nil pointer dereference if called, which is often what you want in a test.

A common real use: embedding sync.Mutex

type Stats struct {
	sync.Mutex
	hits map[string]int
}

func (s *Stats) Hit(page string) {
	s.Lock()
	defer s.Unlock()
	s.hits[page]++
}

This reads well, but it also exports Lock and Unlock as part of Stats's API, so any caller can lock your struct. For types used outside the package, a named field (mu sync.Mutex) keeps the lock private. That trade-off applies to all embedding: everything the embedded type exports becomes part of your type's public surface.

Common mistakes

  • Expecting virtual dispatch. Promoted methods never call the outer type's overrides.
  • Setting promoted fields in a literal. Use the embedded type's name: Admin{User: User{Name: "Ana"}}.
  • Nil embedded pointers. An embedded *T or interface must be set before its methods are called.
  • Accidental API surface. Embedding exports all of the inner type's exported methods on your type.

Frequently Asked Questions

What is struct embedding in Go?

Declaring a field with only a type and no name: type Admin struct { User; Level int }. The embedded User's fields and methods are promoted, so a.Name and a.Greet() work on an Admin directly. The embedded value is still a normal field, reachable as a.User.

Does Go have inheritance?

No. Go has no classes and no subtype inheritance. Embedding gives code reuse through composition: the outer type gets the inner type's methods, but an Admin is not a User. You cannot pass an Admin where a User is expected, and promoted methods cannot call back into the outer type's methods. Polymorphism in Go comes from interfaces.

How do you initialize an embedded struct in Go?

In a composite literal, name the embedded field by its type name: Admin{User: User{Name: "Ana"}, Level: 2}. You cannot set promoted fields directly in the literal: Admin{Name: "Ana"} fails with unknown field Name in struct literal of type Admin.

Can you embed an interface in a struct in Go?

Yes. The struct then satisfies the interface through the embedded value, and you can override individual methods. It is common for decorators and test fakes. If the embedded interface field is nil, calling a method you did not override panics with a nil pointer dereference.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED