Part VII — Expert: internals and design
The last part goes underneath abstractions you've been using without question since Part I: what an interface, a slice, a map, and a goroutine stack actually are in memory. Then it turns from internals to judgment — API design idioms and dependency injection without a framework — before closing with a capstone embedded database engine that draws on nearly everything earlier in the guide, and a final chapter on actually shipping a Go binary.
51. What's really behind an interface value
51.1 Two words, not one
An interface value is a two-word pair: a pointer to type information (the concrete type's method table, called an itab when the interface has methods, or nothing extra for the empty interface's eface), and a pointer/value holding the underlying data.
var w io.Writer = os.Stdout
// conceptually: { itab: *itab-for-(*os.File,io.Writer), data: pointer-to-os.Stdout }
This is exactly why the nil-interface trap from Chapter 9.4 exists: a
nil *MyError stored in an error interface produces a two-word value
{itab: *itab-for-(*MyError,error), data: nil} — the data pointer is
nil, but the interface value as a whole is not, because its type pointer
is set.
51.2 Dynamic dispatch, roughly
Calling a method on an interface value looks up the method in the itab (a small, per-concrete-type-and-interface-pair table built once, cached), then calls through that function pointer with the data word as the receiver — conceptually similar to a C++ vtable call, but resolved per-(type, interface)-pair rather than per-type alone.
51.3 Real-world example
This is why interface method calls have a small but real cost versus a direct concrete-type method call (an extra pointer indirection through the itab) — usually irrelevant, but the reason performance-critical inner loops sometimes avoid an interface-typed hot path in favor of a concrete generic type parameter (Chapter 21), which the compiler can inline directly with no itab lookup at all.