Lesson 8 · Methods, interfaces & generics
Composition & embedding
Go's answer to inheritance — reuse by having, not by being.
Your win: explain embedding and method promotion, say why it's composition rather than inheritance, and read the four ways our repo uses it.
Embedding = a field with no name
Declare a type inside a struct without a field name, and its exported fields and methods are promoted to the outer type — callers use them as if they belonged to the outer struct.1
type commonConsumer struct { ... }
func (c *commonConsumer) handleMessage(...) (bool, error) { ... }
type looseConsumer struct {
commonConsumer // embedded — no field name
}
// looseConsumer now HAS handleMessage(), promoted from commonConsumer.
Your repo embeds in four distinct ways
| Kind | Example in our code | Why |
|---|---|---|
| Struct-in-struct (share behaviour) | commonConsumer in looseConsumer/strictConsumer golibs/kafka/consumer.go:166,204 |
both consumer types reuse retry/handle logic, differ only on commit |
| Interface-in-struct | consumers.ConsumerHandler in SendEmailConsumer spike/.../kafka/send_email_consumer.go:14 |
the struct "is a" handler by delegating to whatever's injected |
| Multiple embeds (value + pointer) | jwt.Claims, *FirebaseClaims, … in CustomClaims golibs/interceptors/auth_claims.go:13 |
merge several claim shapes into one type |
| Interface composition | AllServicer[T] embeds BaseServicer[T] + 5 more golibs/bootstrap/grpc.go:35 |
build a big capability from small interfaces |
npb.UnimplementedNotificationReaderServiceServer
(notification/transports/grpc/notificationmgmt_v1.go:24).
That embed supplies default "not implemented" methods, so when a new RPC is added to
the proto your server still compiles — forward compatibility via embedding.
Interface embedding composes contracts
Interfaces embed too — that's how the standard library builds io.ReadWriter
from io.Reader + io.Writer, and how our
AllServicer[T] is assembled from BaseServicer,
GRPCServicer, KafkaServicer, and friends. Small interfaces
(Lesson 6) compose into bigger capabilities without a single large declaration.
Effective Go — Embedding
The canonical explanation of promotion and why Go chose composition. GOPL ch. 6 goes deeper with worked examples.
→ go.dev/doc/effective_go#embedding
→ The Go Programming Language — ch. 6 (methods)
Check yourself (from memory)
Q1. Embedding a type in a struct (no field name)…
looseConsumer reuses
commonConsumer.
Q2. Go's embedding is best described as…
Q3. Embedding Unimplemented...Server in a gRPC service gives…
looseConsumer reuse commonConsumer's behaviour without inheritance?commonConsumer as an unnamed field, so its
methods (like handleMessage) are promoted onto
looseConsumer — it "has" them, no class hierarchy. looseConsumer
then adds only what differs (its commit behaviour). Composition + promotion, not
inheritance.1. Effective Go — embedding; Go spec — struct types (embedded fields).