Menu
Coddy logo textTech

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 icon

Challenge

Easy

Let'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 ContactInfo struct with OwnerName and Phone fields (both strings)
    • A Vehicle struct that embeds ContactInfo (without a field name) and adds its own Make, Model, and Year fields
    Also create a method called Summary on Vehicle that returns a formatted string containing all the vehicle and contact information.
  • main.go: Read vehicle and owner details from input, create a Vehicle instance with embedded ContactInfo, 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:

  1. The owner name accessed directly on the Vehicle (e.g., v.OwnerName)
  2. The phone accessed through the embedded type (e.g., v.ContactInfo.Phone)
  3. The full summary from your Summary method

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-1234

Notice 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
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming