02 · Core Language
Structs, interfaces, pointers, slices, maps, defer
implements keyword. Pointers are new but simple.
Structs
A struct is a named set of fields. Think of it as a typed object shape.
type Coupon struct {
ID string
Name string
Status string
}
c := Coupon{ID: "abc", Name: "10% Off", Status: "active"}
fmt.Println(c.Name) // "10% Off"
Methods attach behavior to a struct using a receiver:
func (c Coupon) IsActive() bool {
return c.Status == "active"
}
// Value receiver (c is a copy) — use for read-only methods
// Pointer receiver (*Coupon) — use when the method modifies the struct
Interfaces
An interface defines a set of methods. Any type that has those methods satisfies the interface — no declaration needed.
type Stringer interface {
String() string
}
type Coupon struct { Name string }
func (c Coupon) String() string { return "Coupon: " + c.Name }
// Coupon satisfies Stringer automatically — no "implements" keyword
var s Stringer = Coupon{Name: "10% Off"}
fmt.Println(s.String())
Pointers
Go passes values by copy. To modify the original, pass a pointer.
x := 10
p := &x // p is a *int — pointer to x
*p = 20 // dereference and assign
fmt.Println(x) // 20
Use pointer receivers when a method needs to modify the struct or when the struct is large (avoids copying):
func (c *Coupon) Deactivate() {
c.Status = "inactive" // modifies the original
}
Slices
Slices are dynamic arrays. The zero value is nil, which behaves like an empty slice for append and range.
var names []string // nil slice — safe to use
names = append(names, "Alice")
names = append(names, "Bob")
for i, name := range names {
fmt.Println(i, name)
}
Maps
Maps are key-value stores with string, int, or any comparable type as the key. The zero value is nil — safe to read (returns zero value), but writing to a nil map panics.
scores := map[string]int{
"Alice": 95,
"Bob": 87,
}
// Read — always check if key exists
score, ok := scores["Alice"]
if ok {
fmt.Println(score)
}
// Delete
delete(scores, "Bob")
scores := make(map[string]int).
defer
defer schedules a function call to run when the surrounding function returns. Use it for cleanup.
func readFile(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close() // runs when readFile returns, regardless of how
// ... read file
return nil
}
finally block, but attached to the function instead of a try/catch, and it stacks (multiple defers run in LIFO order).
Key Takeaways
- Structs = typed object shapes. Methods attach via receivers.
- Interfaces are satisfied implicitly — no
implements. - Use pointer receivers (
*T) to modify a struct or avoid copying. - Slices grow with
append(). Maps need initialization before writing. deferruns cleanup when a function returns — use it for file/connection closes.