Structs
By the end of this lesson you'll model real-world things with your own struct types, give them behaviour with methods, change them safely with pointers, build bigger types by composition, and see how a type quietly satisfies an interface — the heart of idiomatic Go.
Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.
Part of the free Go course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
1️⃣ Defining a Struct and Making Instances
A struct (short for "structure") is a type you define to group related values together — like a Person with a Name and an Age . You declare the shape once with type Person struct {' ... '} , then create as many instances (individual values) as you like. You can fill the fields by name (clearest and safest) or by position (you must list them in the exact field order). Any field you leave out gets its type's zero value — 0 for numbers, "" for strings, false for bools.
Your turn. Define a small struct and build one instance using field names. Fill in the blanks marked ___ using the // 👉 hints, then run it and check the output.
2️⃣ Methods: Value vs Pointer Receivers
A method is a function attached to a type. You write it with a receiver in parentheses before the name: func (c Counter) Show() . The receiver type decides whether the method sees the original or a copy. A value receiver (c Counter) gets a copy — perfect for read-only methods, but any change is thrown away. A pointer receiver (c *Counter) works on the original , so changes stick. This value-vs-pointer choice is the single most common Go gotcha, so study both halves of this example.
3️⃣ Pointers: & and *
A pointer holds the memory address of a value rather than the value itself. Two operators do all the work: &x takes the address of x (giving a pointer), and *p dereferences the pointer (giving back the value, which you can read or overwrite). Passing a pointer into a function lets that function change your original value. Go is friendly here: it auto-dereferences struct pointers (write u.Name , not (*u).Name ) and forbids pointer arithmetic, so you can't accidentally walk off into invalid memory.
Your turn — fix a wallet that won't update. The Deposit method currently can't change the balance. Give it a pointer receiver and pass the right amount.
4️⃣ Embedding: Composition over Inheritance
Go has no classes and no inheritance. Instead you compose bigger types out of smaller ones by embedding : list a type inside a struct with no field name . The embedded type's fields and methods are then promoted — you can reach them directly on the outer struct. So an Employee that embeds User can use e.Name instead of e.User.Name , and inherits any methods User defines. This is how Go reuses code: build from parts rather than extending a base class.
5️⃣ A First Look at Interfaces
An interface is a named list of method signatures — a contract that says "any type with these methods qualifies." The big idea in Go: a type satisfies an interface automatically , just by having the right methods. There is no implements keyword and nothing to declare. Below, both Dog and Robot have a Speak() string method, so both are Speaker s — and one function can accept either. You'll go much deeper into interfaces in a later lesson; for now, just notice how loose and flexible this makes your code.
📋 Quick Reference
No blanks this time — just a brief and an outline. Define the struct and its method yourself, then run it and check your output against the expected line. This is exactly the shape of code real Go programs are built from.
Practice quiz
What is a struct in Go?
- A function with state
- A list of methods only
- A named type that groups related fields together
- A pointer alias
Answer: A named type that groups related fields together. A struct is a type that groups related fields under one name.
For type Person struct { Name string; Age int }, what is the zero value of var empty Person?
- {Name: "" Age: 0}
- nil
- {Name: nil Age: nil}
- a compile error
Answer: {Name: "" Age: 0}. Each field takes its type's zero value: "" for string, 0 for int.
What is the safest way to build a struct instance?
- Positional: Person{"Al", 30}
- var p Person only
- new(Person)
- By field name: Person{Name: "Al", Age: 30}
Answer: By field name: Person{Name: "Al", Age: 30}. Field-name literals are order-independent and survive added/reordered fields.
A value receiver method gets what?
- The original struct
- A copy of the struct, so changes are discarded
- A pointer to the struct
- Nothing
Answer: A copy of the struct, so changes are discarded. A value receiver operates on a copy; mutations don't affect the original.
Why might a method not change the struct it was called on?
- It used a value receiver instead of a pointer receiver
- The struct was nil
- Go forbids mutation
- The field was const
Answer: It used a value receiver instead of a pointer receiver. Value receivers work on a copy; use a pointer receiver (e.g. *Wallet) to make changes stick.
What does & do?
- Dereferences a pointer
- Allocates a slice
- Takes the address of a value, producing a pointer
- Compares two values
Answer: Takes the address of a value, producing a pointer. &x takes the address of x, producing a pointer.
What does * do to a pointer p?
- Takes its address
- Dereferences it to read or write the pointed-to value
- Doubles it
- Frees it
Answer: Dereferences it to read or write the pointed-to value. *p dereferences the pointer to reach the value it points at.
Does Go support pointer arithmetic like p++ or p + 4?
- Yes, freely
- Only for arrays
- Only with unsafe always required for structs
- No — Go forbids pointer arithmetic to keep memory safe
Answer: No — Go forbids pointer arithmetic to keep memory safe. Go has no pointer arithmetic, which keeps memory access safe.
How does a type satisfy an interface in Go?
- With an implements keyword
- Automatically, just by having the required methods (structural typing)
- By embedding the interface
- By registering it at runtime
Answer: Automatically, just by having the required methods (structural typing). Satisfaction is automatic: any type with the right methods satisfies the interface.
How does Go reuse code across struct types?
- Class inheritance
- Global mixins
- Embedding (composition over inheritance)
- Macros
Answer: Embedding (composition over inheritance). Go composes types via embedding rather than inheriting from base classes.