Structs
Fundamentals Chapter 7 previewed struct in passing. This chapter covers it properly: defining one, attaching behaviour to it with methods, and the pointer concept that decides whether a method can actually change the struct it's called on. Go has no class keyword and no this — methods and pointers together are how Go achieves what JavaScript's object methods did with this (Fundamentals Chapter 7).
Defining and Creating a Struct
type Person struct { ... } defines a new named type with a fixed set of fields. Field access uses the same dot notation as JavaScript object properties — but unlike a map (Chapter 7), a struct's fields are fixed at compile time: there's no way to add an unplanned field later, and each field can have its own distinct type.
Name and Age start with capital letters deliberately — in Go, capitalisation controls visibility outside the current package (covered fully later). Lowercase struct fields still work everywhere in this course's single-file examples, but capitalised fields are the convention for anything meant to be used elsewhere.
Methods — Functions Attached to a Type
(p Person) between func and the method name is the receiver — it's what makes this an ordinary function into a method that can be called as p.Greet(). p inside the method body plays the same role JavaScript's this played inside an object method (Fundamentals Chapter 7) — but it's an explicit, named parameter here, not an implicit keyword.
Pointers — Why Greet() Can't Change p, But Something Else Can
&age ("address of") produces a pointer — a value that points to where age actually lives in memory, rather than a copy of 35 itself. *agePointer ("dereference") goes the other way: follow the pointer back to the real value. This is a genuinely new concept with no real JavaScript equivalent — every JavaScript variable is already accessed "directly," with no separate address-vs-value distinction to think about.
Why This Matters for Methods
A receiver of type Person (no *) receives a brand-new copy of the struct — any changes inside the method vanish once it returns, the value-passing behaviour every Go function has by default. A receiver of type *Person (a pointer receiver) receives a pointer to the original struct instead, so changes made through it persist. Go automatically handles the & when calling person.HaveBirthdayPointer() — no manual &person.HaveBirthdayPointer() needed.
p.Field and that change should be visible afterward, the receiver must be *Person, not Person.
| JavaScript | Go |
|---|---|
| { name: "Philip", age: 35 } | type Person struct { Name string; Age int } |
| greet: function() { ...this... } | func (p Person) Greet() string { ...p... } |
| this (implicit) | Receiver variable (explicit, named by you) |
| Objects are always reference-shared | Plain receiver = copy; pointer receiver (*Type) = shared |
| No address/value distinction | &value (address), *pointer (dereference) |
Coding Challenges
Define a struct Book with fields Title (string), Pages (int). Create one, then write a method (b Book) Describe() string that returns a sentence combining both fields. Print the result of calling Describe().
📄 View solutionDefine a struct Account with field Balance (float64). Write a method (a *Account) Deposit(amount float64) using a pointer receiver that adds amount to Balance. Create an account, call Deposit twice with different amounts, and print Balance after each call to confirm it actually changes.
📄 View solutionWrite a function double(n *int) that takes a pointer to an int and doubles the value it points to (using dereferencing, not a return value). Declare a variable, pass its address to double, and print the variable afterward to confirm it changed.
📄 View solutionChapter 1 Quick Reference (Intermediate)
- type Name struct { Field type } — defines a fixed-shape record type
- func (receiver Type) MethodName() { } — attaches a method to a type
- &value — gets a pointer (the address) to a value
- *pointer — dereferences a pointer, accessing/changing the real value
- Plain receiver (p Type) — method gets a copy; changes don't persist
- Pointer receiver (p *Type) — method gets the original; changes DO persist
- Go calls automatically handle & when invoking a pointer-receiver method on a plain variable
- Next chapter: interfaces — how Go achieves polymorphism without classes or inheritance