Disclaimer: I am not a native speaker, so please forgive me for a large number of mistakes on the site

Embedded fields in Go

2022-10-23 #programming #go

I decided to document interesting things that I discover while learning something, and this is the first post in a such manner. I like Go programming language and trying to write simple programs in it ( at least one helped me to generate docs for our pl/sql codebase, and I think it may be helpful even for someone else, too), and recently I was puzzled by one single line in the Gin framework when I was trying to understand how its Engine struct is implemented.

It was looked like this:

// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
// Create an instance of Engine, by using New() or Default()
type Engine struct {
	RouterGroup
    ....
    ....
}

Why RouterGroup is written here as a type, without a field name? So, here are my investigations.

Embedded fields

In Go, we can embed one structure into another when we declare a type.

package main

import "fmt"

type User struct {
    Name string
    LastName string
}

type SpecifiedUser struct {
    User // <--- MAY BE VERY FRUSTRATING
    Id uint64
    Subsystem string
}

func main() {
	var u SpecifiedUser

	u.Name = "Alex"

	fmt.Println(u.Name)
}

See, we didn’t declare Name field directly in the SpecifiedUser struct, it was just injected from the User type.

If we run this program, it will print string “Alex”.

What about methods? I mean, if a User struct has, for example, a printUserName method, will SpecifiedUser also has this method? Let’s try:

package main

import "fmt"

type User struct {
    Name string
    LastName string
}

func (u User) printUserName() {
	fmt.Println("User name print")
}

type SpecifiedUser struct {
    User
    Id uint64
    Subsystem string
}

func main() {
	var u SpecifiedUser

	u.printUserName()
}

Run the program:

go run ./go-embedded-structs.go
User name print

Boom! Method also has been embedded! But this was a “stateless” method - it prints just a constant string. Let’s see how it will work if we try to print Name field:

package main

import "fmt"

type User struct {
    Name string
    LastName string
}

func (u User) printUserName() {
	fmt.Println(u.Name)
}

type SpecifiedUser struct {
    User
    Id uint64
    Subsystem string
}

func main() {
	var u SpecifiedUser

	u.Name = "Alex"

	u.printUserName()
}
go run ./go-embedded-structs.go
Alex

Since I know that fields and methods are injected, it’s not a big surprise, but still interesting and important result.

When a parent struct has the same field name as an embedded

My next question was: “What if there are fields with the same names?”. I thought that Go will throw an error, but I was wrong:

package main

import "fmt"

type User struct {
    Name string
    LastName string
}

func (u User) printUserName() {
	fmt.Println(u.Name)
}

type SpecifiedUser struct {
    User
    Name string
    Id uint64
    Subsystem string
}

func main() {
	var u SpecifiedUser

	u.Name = "Alex"

	u.printUserName()
}

However, this program prints nothing. But if we try to print Name field manually, it works:

func main() {
	var u SpecifiedUser

	u.Name = "Alex"

	u.printUserName()
	fmt.Println(u.Name)
}

Program with this main function prints this:


Alex

Final thoughts

Field embedding is a great way to compose new types out of already existed ones, and it really looks elegant, like almost everything in Go that I know at this moment.