How is an interface value represented at runtime? What is the difference between iface and eface?
Question 77HardGo 1.22 to 1.25
An interface value is a two-word structure. The runtime uses two layouts:
- eface (empty interface,
any):{_type *_type; data unsafe.Pointer}. It holds only the dynamic type descriptor and a pointer to the data. - iface (non-empty interface):
{tab *itab; data unsafe.Pointer}. Theitabholds the interface type, the concrete type, a hash and the table of method pointers used for dynamic dispatch.
// simplified from runtime/runtime2.go (itab lives in internal/abi as abi.ITab since Go 1.22)
type eface struct {
_type *_type
data unsafe.Pointer
}
type iface struct {
tab *itab
data unsafe.Pointer
}
type itab struct {
inter *interfacetype // the static interface type
_type *_type // the dynamic (concrete) type
hash uint32 // copy of _type.hash, used by type switches
fun [1]uintptr // variable-sized method table; fun[0]==0 means T does not implement inter
}
If the concrete value is pointer-shaped (a pointer, map, chan, func, or single-pointer struct), data holds it directly. Otherwise the value is copied to the heap, or to static read-only data for constants, zero-sized values and small integers, and data points at the copy. An interface is nil only when both words are nil.
What the interviewer wants to hear: the two-word layout, that storing a value in an interface copies it, and how this explains the "nil interface vs typed nil" bug.
More on Interfaces, Methods & Embedding
- Q78What is an itab, when is it built, and what does a method call through an interface cost?
- Q79What does this print? (nil interface vs interface holding a nil pointer)
- Q80Why does this function return a non-nil error even when nothing failed? How do you fix it?
- Q81Explain method sets. Why does
*Tsatisfy an interface whenTdoes not? - Q82Why doesn't
m["k"].Inc()compile whenInchas a pointer receiver? What other values are not addressable? - Q83How do you decide between value and pointer receivers?