Go
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions and Methods
-
Section 4: Concurrency
-
Section 5: Packages and Tooling
-
Section 6: More Standard Library
-
Section 7: Building Services
-
Section 8: Advanced Go
-
Section 9: Go in the Cloud-Native Ecosystem
-
Section 10: Data Structures and Algorithms in Go
-
Section 11: Testing and Deployment
-
Section 12: Practical Projects
-
Section 13: More Standard Library Practice
-
Section 14: More Practice Projects
-
Section 15: Design Patterns in Go
-
Section 16: Interview Practice
-
Section 17: Package fmt In Depth
-
Section 18: Package strings and strconv
-
Section 19: Package os and io
-
Section 20: Package time
-
Section 21: Package sort and container
-
Section 22: Package encoding
-
Section 23: Package net/http In Depth
-
Section 24: Package context
-
Section 25: Package regexp and bytes
-
Section 26: Package errors In Depth
-
Section 27: Package crypto and hash
-
Section 28: Package flag and log
-
Section 29: Package sync In Depth
-
Section 30: More Practice Exercises
-
Section 31: Go Modules and Workspaces In Depth
-
Section 32: Generics Deep Dive (Go 1.18+)
-
Section 33: Testing Package In Depth
-
Section 34: More Interview and Whiteboard Practice
-
Section 35: Package math and unicode
-
Section 36: Package path and filepath
-
Section 37: Package database/sql
-
Section 38: Package text/template and html/template
-
Section 39: Package archive and compress
-
Section 40: Lower-Level net Package
-
Section 41: Package runtime and reflect
-
Section 42: Package embed and io/fs
-
Section 43: Go Assembly and CGO Basics
-
Section 44: Building CLIs and TUIs
-
Section 45: Go Performance Tuning
-
Section 46: More Real-World Projects
-
Section 47: Go in Production
-
Section 48: Go Design Patterns
-
Section 49: Go Interfaces Deep Dive
-
Section 50: Final Practice and Review
182: Common Go Interview Questions on Struct Embedding
If you're prepping for a Go interview, someone is almost certainly going to ask you about struct embedding. Usually, they're trying to bait you into saying the word "inheritance." I've seen a lot of senior devs stumble here because they come from Java or C# and their brain instinctively goes to class hierarchies. But Go doesn't do inheritance. It does composition via embedding.
Let's actually play this out in the editor. I want to build a simple system for a smart home. We'll have a basic Device and a specific Light. I'll start by embedding the device into the light and see how the compiler handles it.
Wait, is this just inheritance?
package main
import "fmt"
type Device struct {
Brand string
ID int
}
type Light struct {
Device // Embedding
Brightness int
}
func main() {
l := Light{
Device: Device{Brand: "Philips", ID: 123},
Brightness: 80,
}
// Look at this: I'm accessing Brand directly on l
fmt.Println("Brand:", l.Brand)
}
Now, at first glance, l.Brand looks like Light inherited the field from Device. But that's a lie. What's actually happening is "promotion." Go sees that Light doesn't have a Brand field, so it looks into the embedded Device and promotes that field to the top level for convenience. It's just syntactic sugar. I could still write l.Device.Brand if I wanted to be explicit.
When things get messy: Shadowing
Interviewer's love to ask what happens when the outer struct and the embedded struct have the same field or method. Let's break my code on purpose to find out.
type Device struct {
Brand string
}
func (d Device) PowerOn() {
fmt.Println("Device is now on")
}
type Light struct {
Device
Brand string // Shadowing the embedded field
}
func (l Light) PowerOn() {
fmt.Println("Light is now on")
}
func main() {
l := Light{
Device: Device{Brand: "Generic"},
Brand: "Hue",
}
fmt.Println(l.Brand) // Which one prints?
l.PowerOn() // Which one runs?
}
If you run this, l.Brand will be "Hue" and it'll print "Light is now on." The outer struct always wins. This is called shadowing. The embedded Device is still there, and its data is still intact, but it's hidden behind the Light's own definitions. If I ever need that original brand, I have to go deep: l.Device.Brand.
The "Who am I?" receiver problem
Here is the real killer question. This is where most candidates fail. I'm going to add a method to the Device that tries to use a field from the Light. Watch what happens.
type Device struct {
Brand string
}
func (d Device) Describe() {
// I want to print the Brand, and maybe the Brightness?
// But wait... I'm a Device. I don't know what a Light is.
fmt.Printf("Brand: %s\n", d.Brand)
}
type Light struct {
Device
Brightness int
}
func main() {
l := Light{
Device: Device{Brand: "Lutron"},
Brightness: 100,
}
l.Describe()
}
The code above compiles and runs fine, but Describe() can only see d.Brand. It has absolutely no access to l.Brightness. Why? Because the receiver of Describe() is Device, not Light.
Even though I called l.Describe(), the method was promoted from Device. When that method executes, the receiver is just the embedded Device struct. It has no idea it's being embedded inside a Light. In a true inheritance language, the base class method could often be overridden or use polymorphism to see the child's state. In Go, the embedded struct is totally oblivious to its parent. It's a one-way street.
📋 Practical Task
Fixing the SmartHome Logger Shadowing Bug
You are reviewing a teammate's code for a smart home system. They tried to implement a Sensor that embeds a BaseComponent. However, they've encountered a bug: the LogStatus method is printing the wrong information because of field shadowing, and the BaseComponent method cannot access the Sensor's specific Reading value.
Your Task:
- Modify the
Sensorstruct and its methods so that callings.LogStatus()prints theNamefrom theBaseComponentAND the currentReadingfrom theSensor. - Ensure that you resolve the shadowing conflict where both
BaseComponentandSensorhave a field namedStatus, making sure theSensor's status is the one prioritized. - Since
BaseComponent.LogStatuscannot seeSensorfields, you must overrideLogStatuson theSensorstruct to achieve the desired output.
package main
import "fmt"
type BaseComponent struct {
Name string
Status string
}
func (bc BaseComponent) LogStatus() {
fmt.Printf("Component %s is %s\n", bc.Name, bc.Status)
}
type Sensor struct {
BaseComponent
Status string
Reading float64
}
func main() {
s := Sensor{
BaseComponent: BaseComponent{
Name: "Temperature Sensor",
Status: "Initializing",
},
Status: "Active",
Reading: 22.5,
}
// This currently only prints "Component Temperature Sensor is Initializing"
// because it's using the promoted BaseComponent method.
// FIX THIS to print: "Sensor Temperature Sensor [Active] reads 22.5"
s.LogStatus()
}
There are no comments for now.