skip to content
Alvin Lucillo

Outer and inner type promotion in interfaces

/ 2 min read

💻 Tech

In Go, there’s a concept of embedding that allows an outer type to access properties and methods by an inner type. This is different from other languages’ concept of inheritance where there’s a subtype. Go only does “promotion” of properties and methods, and it does not make one type a type of another.

In the code below, employee is embedded in department, that’s why an object of type department can access method report() of employee because the properties and methods are promoted from inner type (employee) to the outer type (department). We can see it in three ways:

  1. Direct access from department
  2. Direct access from employee
  3. Access via a polymorphic function

The polymorphic function already knows that the function is from the employee because, again, the methods are promoted to the outer type even though we passed a copy of department to the function. What will happen if two structs have the same implementation? See the 2nd version below.

type employee struct{}
type department struct {
	employee
}

func (employee) report() {
	fmt.Println("employee reporting")
}

type someinterface interface {
	report()
}

func main() {
	d := department{
		employee: employee{},
	}

	d.report()
	d.employee.report()
	report(&d)
}

func report(s someinterface) {
	s.report()
}
employee reporting
employee reporting
employee reporting

In the 2nd version of the code below, department has its own implementation. In the output, we can see that the outer type (department) takes precendence over the implementation of the inner type (employee). But if you access the inner type directly, you can use its implementation. In addition, the polymorphic function uses the outer type’s implementation. In other words, if there are implementations in the outer and inner types, accessing the method via outer type and polymorphic function will prioritize the outer type’s implementation.

type employee struct{}
type department struct {
	employee
}

func (employee) report() {
	fmt.Println("employee reporting")
}

func (department) report() {
	fmt.Println("department reporting")
}


type someinterface interface {
	report()
}

func main() {
	d := department{
		employee: employee{},
	}

	d.report()
	d.employee.report()
	report(&d)
}

func report(s someinterface) {
	s.report()
}
department reporting
employee reporting
department reporting