CodeRigor

FREE LESSON

Struct methods & small interfaces

Choose value or pointer receivers deliberately and consume behavior through an implicitly satisfied interface.

A struct groups named fields into a value. Assigning an Entry copies its fields; copying a pointer to an Entry shares one record. That distinction remains even though Go manages memory lifetime. Garbage collection does not make copies independent, and it does not decide which operations your API should allow to mutate a caller's object.

Methods add behavior to a named type. A value receiver receives a copy; a pointer receiver can update the referenced record. Choose the receiver from the method's semantics. The build's Add changes Hits and needs *Entry. Label only reads the record and can use Entry. Go can automatically take the address of an addressable value for a method call, but interface satisfaction still depends on the actual method set of the assigned value.

Small interface, multiple implementations
start / 4
The consumer requires Label, while each concrete type chooses how to compute it.
Small interface, multiple implementations
The consumer requires Label, while each concrete type chooses how to compute it.

An interface belongs at the consuming boundary

An interface describes required methods, and a type satisfies it implicitly. There is no implements declaration and no requirement to inherit from a base class. A consumer needing only a label can accept interface { Label() string } through a named Labeled interface. This is a behavior contract; it does not promise that every implementation stores a Name field.

Go
package main
import "fmt"
type Entry struct { Name string; Hits int }
func (e *Entry) Add(n int) { e.Hits += n }
func (e Entry) Label() string { return fmt.Sprintf("%s:%d", e.Name, e.Hits) }
type Labeled interface { Label() string }
func main() {
    a := Entry{Name: "api", Hits: 1}
    b := a
    b.Add(2)
    var item Labeled = a
    fmt.Println(item.Label(), b.Label())
}

The output is api:1 api:3. The record copy separated the numeric field, while the interface lets the consumer call the promised method. Because Label has a value receiver, both Entry and *Entry supply it. A method declared only on *Entry would not be present in Entry's method set.

A value copy separates scalar fields
start / 2
Add uses a pointer to b; a retains its original Hits value.
A value copy separates scalar fields
Add uses a pointer to b; a retains its original Hits value.

Keep the interface small and the nil policy explicit

Do not assert every Labeled value back to Entry just to recover its fields. That rejects unrelated valid implementations and duplicates their behavior. Call Label and preserve the input order. The tests include another type with the same method precisely to exercise this substitution.

An interface can contain a dynamic type and a nil pointer of that type; such an interface is not equal to nil. Calling a method may then panic if its receiver does not handle nil. The build excludes nil receivers explicitly, so you do not need to invent recovery behavior. The conceptual checks still ask you to recognize this distinction before using interface nil checks in a real API.

Use the fill to collect behavior through the interface, then repair one receiver bug and one ordering bug. Verify Add with zero and negative deltas and compare the original record with a modified copy.

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →

Focus on methods and structural interfaces. Compare those examples with the tested value/pointer method sets here; concurrency is outside this unit.