Struct Embedding Basics
Part of the Object Oriented Programming section of Coddy's GO journey — lesson 35 of 107.
Struct embedding is Go's mechanism for including one struct type inside another. Instead of using a field name, you simply specify the type, and Go promotes the embedded struct's fields to the outer struct.
Here's the basic syntax:
type Address struct {
City string
Country string
}
type Person struct {
Name string
Address // embedded - no field name
}
When you embed Address into Person, you can access the embedded fields directly on the outer struct:
func main() {
p := Person{
Name: "Alice",
Address: Address{
City: "London",
Country: "UK",
},
}
fmt.Println(p.City) // London - direct access
fmt.Println(p.Country) // UK - direct access
fmt.Println(p.Address.City) // also works
}
Notice how p.City works without explicitly writing p.Address.City. Go automatically promotes the embedded struct's fields, making them accessible as if they belonged to the outer struct. You can still access them through the embedded type name when needed.
This differs from regular nested structs where you must always use the field name to access inner fields. Embedding creates a flatter, more convenient API while still maintaining the logical grouping of related data.
Challenge
EasyLet's build a vehicle registration system that uses struct embedding to create a clean, organized data model. You'll separate contact information from vehicle details while allowing direct access to all fields through embedding.
You'll organize your code across two files:
vehicle.go: Create two structs that work together through embedding:- A
ContactInfostruct withOwnerNameandPhonefields (both strings) - A
Vehiclestruct that embedsContactInfo(without a field name) and adds its ownMake,Model, andYearfields
SummaryonVehiclethat returns a formatted string containing all the vehicle and contact information.- A
main.go: Read vehicle and owner details from input, create aVehicleinstance with embeddedContactInfo, and demonstrate accessing the embedded fields both directly and through the embedded type name. Print the vehicle summary at the end.
The following inputs will be provided:
- Line 1: Owner name
- Line 2: Phone number
- Line 3: Vehicle make
- Line 4: Vehicle model
- Line 5: Year (integer)
Your Summary method should return a string in this format:
[Year] [Make] [Model] - Owner: [OwnerName], Phone: [Phone]In your main file, demonstrate field promotion by printing:
- The owner name accessed directly on the Vehicle (e.g.,
v.OwnerName) - The phone accessed through the embedded type (e.g.,
v.ContactInfo.Phone) - The full summary from your
Summarymethod
For example, given Alice Smith, 555-1234, Toyota, Camry, and 2023, your output should be:
Alice Smith
555-1234
2023 Toyota Camry - Owner: Alice Smith, Phone: 555-1234Notice how both access patterns work—direct access through field promotion and explicit access through the embedded type name—giving you flexibility in how you work with embedded data.
Cheat sheet
Struct embedding allows you to include one struct type inside another without specifying a field name. The embedded struct's fields are promoted to the outer struct, making them directly accessible.
Basic Syntax
type Address struct {
City string
Country string
}
type Person struct {
Name string
Address // embedded - no field name
}
Accessing Embedded Fields
You can access embedded fields in two ways:
p := Person{
Name: "Alice",
Address: Address{
City: "London",
Country: "UK",
},
}
// Direct access (field promotion)
fmt.Println(p.City) // London
// Through embedded type name
fmt.Println(p.Address.City) // London
Go automatically promotes the embedded struct's fields, allowing direct access as if they belonged to the outer struct. You can still access them through the embedded type name when needed.
Try it yourself
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Read owner name
ownerName, _ := reader.ReadString('\n')
ownerName = strings.TrimSpace(ownerName)
// Read phone number
phone, _ := reader.ReadString('\n')
phone = strings.TrimSpace(phone)
// Read vehicle make
make, _ := reader.ReadString('\n')
make = strings.TrimSpace(make)
// Read vehicle model
model, _ := reader.ReadString('\n')
model = strings.TrimSpace(model)
// Read year
yearStr, _ := reader.ReadString('\n')
yearStr = strings.TrimSpace(yearStr)
year, _ := strconv.Atoi(yearStr)
// TODO: Create a Vehicle instance with embedded ContactInfo
// Use the values read from input above
// TODO: Print the owner name accessed directly on the Vehicle (field promotion)
// Example: fmt.Println(v.OwnerName)
// TODO: Print the phone accessed through the embedded type name
// Example: fmt.Println(v.ContactInfo.Phone)
// TODO: Print the full summary using the Summary method
// Example: fmt.Println(v.Summary())
// Remove these lines once you implement the solution
_ = ownerName
_ = phone
_ = make
_ = model
_ = year
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of Go OOP
External FilesGo Workspace & ModulesPackages & ImportsExported vs Unexported NamesIntroduction to OOP in GoStructs as ClassesDefining Methods on StructsPointer vs Value ReceiversStruct InitializationConstructor FunctionsRecap - Simple Calculator4Interfaces
Introduction to InterfacesImplicit ImplementationInterface as ContractEmpty Interface (any)Type AssertionType SwitchInterface CompositionStringer & Error InterfacesRecap - Shape Calculator7Encapsulation
Exported vs Unexported FieldsPackage-Level EncapsulationGetter & Setter MethodsInformation Hiding in GoRecap - Student Records10Generics (Go 1.18+)
Introduction to GenericsType ParametersType ConstraintsGeneric StructsGeneric Methods WorkaroundRecap - Generic Collection2Types & Structs Deep Dive
Basic & Composite TypesCustom Type DefinitionsStruct TagsAnonymous StructsNested StructsZero Values & DefaultsRecap - Contact Book5Composition Over Inheritance
Why Go Has No InheritanceStruct Embedding BasicsMethod PromotionEmbedding Multiple StructsEmbedding vs AggregationShadowing Embedded MethodsRecap - Employee Hierarchy8Error Handling & OOP
The error InterfaceCustom Error TypesError Wrapping (fmt.Errorf)Sentinel Errorserrors.Is() and errors.As()Panic, Defer, and RecoverRecap - File Parser3Pointers & Memory
Pointer Basics in GoPointers to StructsPass by Value vs ReferenceThe new() FunctionGarbage Collection in GoRecap - Linked List Builder6Polymorphism in Go
Polymorphism via InterfacesDuck Typing in GoInterface Satisfaction RulesPolymorphic CollectionsDependency InjectionRecap - Payment Processor9Concurrency & OOP
Goroutines BasicsChannels & CommunicationBuffered vs Unbuffered ChanSelect Statementsync.Mutex & sync.RWMutexsync.WaitGroupThread-Safe Struct DesignRecap - Worker Pool