2025-08-05

Be Careful with Go Struct Embedding


Go has a feature called struct embedding that allows you to compose types. It looks something like this:

type Position struct {
	X int
	Y int
}

type Colour struct {
	R byte
	G byte
	B byte
}

type Rectangle struct {
	Position
	Colour
	
	Width  int
	Height int
}

r := Rectangle{/* ... */}

// This works:
fmt.Printf("%d,%d\n", r.Position.X, r.Position.Y)

// but so does this:
fmt.Printf("%d,%d\n", r.X, r.Y)

But what do you think this code does?

type FooService struct {
	URL string
}

type BarConnectionOptions struct {
	URL string
}

type BarService struct {
	BarConnectionOptions
}

type Options struct {
	FooService
	BarService
}

opts := Options{
	FooService: FooService{URL: "abc.com"},
	BarService: BarService{
		BarConnectionOptions: BarConnectionOptions{
			URL: "xyz.com",
		},
	},
}

fmt.Println(opts.URL)

I would expect this to fail to compile as URL is ambiguous. It actually prints abc.com, presumably as it is the least nested version of that field. This happened at the day job, although it was caught in a test. Be careful when embedding structs!