Skip to content
Theory · Structs and methods · lesson 8 of 11

Embedding

Picture an employee form with a whole personal form glued into it: name, surname and the rest. On top of it, the employee form has one more field, "Company". HR reads the name straight off the employee sheet without first looking for the glued-in page.

Go works the same way. The type Employee should do everything a Person can and also store a company name. If you put Person into an ordinary field p Person, every access gets longer: e.p.Name, e.p.Greet(). The code works, but the name p adds nothing to such accesses. In Go a type can be embedded in a struct, and then its fields and methods are reachable directly:

GO
package main

import "fmt"

type Person struct{ Name string }
type Employee struct {
    Person
    Company string
}

func (p Person) Greet() string { return "Hi, " + p.Name }
func main() {
    e := Employee{Person: Person{Name: "Ann"}, Company: "Abyss"}
    fmt.Println(e.Name, e.Greet()) // Ann Hi, Ann
}

Let's walk through the example line by line:

  1. Employee starts with a field that has no name, only the type Person.
  2. The literal fills in the glued-in form with the value Person{Name: "Ann"}, and the field Company with the string "Abyss".
  3. e.Name reads the name straight from e.
  4. e.Greet() calls the method of Person, so the program prints Ann Hi, Ann.

The embedded field

A field given only a type, with no name, is called an embedded field, and the technique itself is embedding. The fields and methods of the embedded type are promoted to the outer struct:

  • a field: e.Name means e.Person.Name;
  • a method with a value receiver: e.Greet() means e.Person.Greet();
  • a method with a pointer receiver: if Greet took a *Person, the call e.Greet() on the variable e would still work.

An embedded field is part of the struct, like any other field. It has a name too, and that name is the name of its type. Two things follow:

  • the literal says Person: Person{Name: "Ann"};
  • the whole Person value is reached as e.Person.

If the embedded field is left out of the literal, it gets its zero value: in Employee{Company: "Abyss"} the field Name is empty.

Shadowing names

An own field of the outer struct with the same name shadows the promoted one. An own method Greet would hide the promoted one the same way. The embedded field stays reachable through the type's name:

GO
type Manager struct {
    Person
    Name string
}
GO
m := Manager{Person: Person{Name: "Ann"}, Name: "Boss"}
fmt.Println(m.Name, m.Person.Name, m.Greet()) // Boss Ann Hi, Ann

Let's see what the second line prints:

  1. m.Name is the own field of Manager, so Boss.
  2. m.Person.Name is the field of the embedded Person, so Ann.
  3. m.Greet() returns Hi, Ann: the method Greet is declared on Person and sees only the fields of Person.

The method Greet does not know about the Manager type's own field Name. The glued-in form has no idea it sits inside another one.

Not inheritance

In languages with inheritance, a derived type counts as its base type and can stand in for it. Embedding does not work that way: Employee contains a Person value but does not become a Person itself. A method of the embedded type always works with its own value and never calls the methods of the outer type.

Interviewers like to ask: "Is embedding in Go inheritance?" The answer is no:

  • the outer struct contains the inner one as an ordinary field;
  • the inner one's fields and methods are reachable by a shorter path;
  • Employee does not become a Person and cannot stand in for it.

This is also where the form picture ends: HR may accept an employee sheet as a personal form, but the compiler will not.

Common mistake

An Employee is passed where a Person is expected:

GO
func greet(p Person) string { return "Hello, " + p.Name }

func main() {
    e := Employee{Person: Person{Name: "Ann"}, Company: "Abyss"}
    fmt.Println(greet(e)) // ✗
}
TEXT
cannot use e (variable of struct type Employee) as Person value in argument to greet

The program does not compile: Employee and Person are different types, even when one is embedded in the other. Pass the embedded field explicitly: greet(e.Person).

Key points

  • An embedded field is a field with only a type and no name; its fields and methods are promoted to the outer struct.
  • An own field or method with the same name shadows the promoted one, and the embedded value is reachable as e.Person.
  • Embedding is not inheritance: an Employee does not become a Person and cannot be passed in its place.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way