Overview

Appendix D. Object-oriented programming

Go approaches object-oriented programming with a focus on clarity and behavior rather than class hierarchies. Instead of classes and inheritance, Go lets you attach methods to any concrete type and uses interfaces for polymorphism, which types satisfy implicitly by implementing the required methods. This emphasis on simple, decoupled design encourages starting with concrete types, discovering interfaces only when consumers need to switch between implementations, and leveraging generics cautiously to avoid unnecessary complexity.

Structs are lightweight, transparent data holders with zero values and well-defined underlying types; named types remain distinct even if their underlying types match, requiring explicit conversions. Methods use receivers: value receivers work on copies and encourage immutable patterns by returning new values, while pointer receivers can mutate the original value. Because calling methods always passes copies, mixing receiver kinds on the same type can hurt usability and even cause race conditions in concurrent code; choose receiver types consistently and prefer value receivers for small or immutable data, and pointer receivers when external mutation is required.

Interfaces express shared behavior without explicit declarations from implementing types; a single function can operate over any type that satisfies an interface, and composite designs like a multi-notifier demonstrate how interfaces enable flexible composition. Internally, interface values carry a dynamic type and value, and method-set rules matter: T exposes only value-receiver methods while *T exposes both, so pointer-receiver methods require pointer values to satisfy interfaces. Generics complement interfaces by enabling reusable, type-safe functions constrained by sets of types (including those with identical underlying types), but the guidance remains to prefer the simplest tool—interfaces or generics—only when they reduce duplication without adding needless indirection.

Figure D.1 The server.check method receives an fsrv copy as s. The original fsrv (a *server) and the receiver s (a *server) point to the same server value in memory.
Figure D.2 multiNotifier forwards a notify call to the notifiers it has.
Figure D.3 A slackNotifier value appears as a notifier value within the function.

FAQ

How is object-oriented programming in Go different from class-based languages?Go has no classes or inheritance. Instead, it focuses on behavior: you attach methods to your own types (including ones based on ints, maps, slices, and funcs), and use interfaces for polymorphism. Types satisfy interfaces implicitly by implementing their methods—no explicit “implements” needed.
What are structs in Go, and do they hide anything?Structs are lightweight, plain data containers with named fields. They don’t have inheritance or hidden pointers—what you see is what you get. Go may insert padding for alignment, but otherwise there’s nothing implicit.
What’s the difference between value and pointer receivers?Both receive a copy of the receiver, but: - Value receiver methods operate on a copy; mutations are not visible to the caller. - Pointer receiver methods receive a copy of a pointer, so they can mutate the original value that the pointer refers to. Changes are visible to the caller.
When should I choose value receivers vs pointer receivers?Use value receivers for small, immutable, or frequently created values. Use pointer receivers when you need to mutate the original value or avoid copying large structs. Keep method sets consistent across a type and avoid premature optimization—measure before choosing for performance.
Why should I avoid mixing receiver types on the same type?It’s harder to use and can cause race conditions. A value-receiver method copies the whole struct; if another goroutine mutates fields via a pointer-receiver method at the same time, you can race when the copy reads those fields. Prefer consistent receiver types for a given type.
Can I add methods to any type? Across packages?You can add methods only to types you declare in the same package. If you need methods on a foreign or built-in type, declare a new named type whose underlying type is that type (for example, type MyInt int), then add methods to your named type.
How do interfaces work in Go, and when should I declare them?Interfaces describe behavior (method sets). Types satisfy them implicitly by having the required methods. Start with concrete types and “discover” interfaces when a consumer (like a function) needs to work with multiple concrete types. Keep interfaces small and focused.
Why won’t a T value satisfy an interface that requires pointer-receiver methods?Method sets differ: T has only value-receiver methods; *T has both value- and pointer-receiver methods. If an interface requires a method defined with a pointer receiver, you must pass a *T to satisfy it; a T doesn’t have that method in its method set.
What’s inside an interface value at runtime?An interface value stores two things: the dynamic type and the dynamic value. The value part is a copy—either a direct copy of the value or a copy of a pointer to the original. Go’s pass-by-value semantics still apply, which affects which methods are callable through the interface.
How do generics fit in, and what does the tilde (~) mean in constraints?Generics let you write functions that work across types, constrained by interfaces. A constraint like interface{ ~int | ~int64 | ~float64 } means “types whose underlying types are int, int64, or float64.” Use generics when they simplify code; prefer simple interfaces if generics add unnecessary complexity.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Go by Example ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Go by Example ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Go by Example ebook for free