- 原文: https://research.swtch.com/interfaces ## Usage ```go type Stringer interface { String() string } type Binary uint64 func (i Binary) String() string { return strconv.Uitob64(i.Get(), 2) } func (i Binary) Get() uint64 { return uint64(i) } ``` - Binary 新类型实现了 Stringer 接口。 ## Interface Values - interface 的值有两个字长值(指针)组成: - 一个指针,指向存储的类型信息,interface table or itable。 - 一个指针,指向实际的数据(concrete type)。 <img src="https://img.jonahgao.com/oss/note/2025p1/golang_iface_struct.png" width="486" height="238" /> - **itable** - 包含了类型信息和函数指针。 - 函数指针只包含 interface 的方法,例如 Binary 的 Add 方法就没有在内。 - **data pointer** - 指向实际的数据,本例中指向的是 b 的一份拷贝。 - `var s Stringer = b` 制作了 b 的一份拷贝,而非指针。 - 执行 type switch 时,检查 `s.itable->type` 是否是目标类型。 - 执行 `s.String()` 时,等价于`s.itable->fun[0](s.data)`。 ## Computing the Itable - itable 里的值是在**运行时**计算的。 1. compiler 为每一个 concrete type 生成了一个 type description structure,其中包含了该类型的 method list。 2. compiler 也为每一个 interface type 生成了 type description structure,结构不同,但是也包含了它的 method list。 3. 当对一个 interface 赋值时,开始计算它的 itable,根据 interface type 的 method list 去查找 concrete type 的 method list。 - 每次生成 itable 后 runtime 会进行缓存,所以只需要计算一次。 ## Memory Optimizations - 如果 interface 类型没有 method,例如 `interface{}`,不分配函数指针部分的内存。 - 一个 interface 是否包含方法可以在静态期判断,从源码层面。 - <img src="https://img.jonahgao.com/oss/note/2025p1/golang_iface_without_method.png" height="170" /> - 如果 concrete type 很小,可以直接放在 interface 中,不需要额外增加一个指针,避免 indirect。 - <img src="https://img.jonahgao.com/oss/note/2025p1/golang_iface_direct.png" width="418" height="238" />