Updated Aug 8, 2026

08 - Structs: Your Own Types

📋 Jump to Takeaways

🎁 A user has a name, an email, and an age. Are you going to pass three separate variables to every function? What if you could bundle them into a single custom type?

A rectangle has a width and a height. A person has a name and an age. You could use separate variables for each, but that falls apart fast. What if you have ten rectangles? Twenty people? You need a way to group related data together. In Go, that's a struct.

Defining a Struct

type Rectangle struct {
	Width  float64
	Height float64
}

type creates a new type. Rectangle is the name. struct means it's a group of fields. Each field has a name and a type.

Creating a Struct

r := Rectangle{
	Width:  10.0,
	Height: 5.0,
}
fmt.Println(r) // {10 5}

You can let Go fill in zero values for fields you don't set:

r := Rectangle{Width: 10.0}
fmt.Println(r.Height) // 0

Accessing Fields

Use a dot:

r := Rectangle{Width: 10.0, Height: 5.0}

fmt.Println(r.Width)  // 10
fmt.Println(r.Height) // 5

r.Width = 20.0
fmt.Println(r.Width)  // 20

Structs in Functions

Functions receive copies. To modify a struct, return the updated version:

func scale(r Rectangle, factor float64) Rectangle {
	r.Width = r.Width * factor
	r.Height = r.Height * factor
	return r
}

func main() {
	r := Rectangle{Width: 10, Height: 5}
	r = scale(r, 2)
	fmt.Println(r) // {20 10}
}

Methods

A method is a function attached to a type. Instead of area(r), you write r.Area():

func (r Rectangle) Area() float64 {
	return r.Width * r.Height
}

func main() {
	r := Rectangle{Width: 10, Height: 5}
	fmt.Println(r.Area()) // 50
}

The (r Rectangle) before the function name is called a receiver. It means "this method belongs to the Rectangle type."

Methods that need to modify the struct return the modified copy. In Go Essentials (the next course), you'll learn pointers, which give you a cleaner way to do this:

func (r Rectangle) Scale(factor float64) Rectangle {
	r.Width = r.Width * factor
	r.Height = r.Height * factor
	return r
}

Multiple Struct Types

type Person struct {
	Name string
	Age  int
}

type Address struct {
	Street string
	City   string
}

Each type is independent. You can use them in the same program without any conflict.

Slices and Maps of Structs

people := []Person{
	{Name: "Alice", Age: 30},
	{Name: "Bob", Age: 25},
}

contacts := map[string]Person{
	"alice": {Name: "Alice", Age: 30},
}

Key Takeaways

  • type Name struct { } defines a new type with named fields
  • Access fields with a dot: p.Name, r.Width
  • Structs get zero values for unset fields
  • Functions receive copies of structs. Return the modified struct to update it
  • Methods attach functions to types: func (r Rectangle) Area() float64
  • Call methods with dot syntax: r.Area()
  • Structs work in slices ([]Person) and maps (map[string]Person)
  • If three variables always travel together, they belong in a struct

🎁 Your program works perfectly until a file is missing or a user types garbage. What happens then? Most languages crash with an exception. Go does something different.

🚀 Ready to run?

Complete runnable examples for this lesson.

📝 Ready to test your knowledge?

Answer the quiz below to mark this lesson complete.

Spot something off? Report an issue