Skip to content

Commit abee045

Browse files
committed
update interface
1 parent 618ad38 commit abee045

File tree

1 file changed

+85
-2
lines changed

1 file changed

+85
-2
lines changed

interface.md

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,16 +142,99 @@ func CreateOrder(order OrderStruct) {
142142

143143
### eface 适用场景
144144

145-
### eface 实现原理
145+
举个标准库里的例子:
146+
147+
```go
148+
func Slice(slice interface{}, less func(i, j int) bool)
149+
```
150+
151+
标准库的 container package,因为不知道用户会在 container 中存储什么类型,所以大部分 API 也是用 interface{} 的。
152+
153+
除了标准库,我们在 fmt.Print 系列和 json.Unmarshal 中也会见到 interface{}。
154+
155+
```go
156+
func Println(a ...interface{}) (n int, err error)
157+
```
158+
159+
```go
160+
func Unmarshal(data []byte, v interface{}) error
161+
```
162+
163+
总结一下,使用 interface 主要是我们在不知道用户的输入类型信息的前提下,希望能够实现一些通用数据结构或函数。这时候便会将空 interface{} 作为函数的输入/输出参数。在其它语言里,解决这种问题一般使用泛型。
146164

147165
## 类型转换
148166

149167
### 普通类型转换为 eface
150168

151-
### 普通类型转换为 iface
169+
```go
170+
1 package main
171+
2
172+
3 var i interface{}
173+
4
174+
5 func main() {
175+
6 var y = 999
176+
7 i = y
177+
8 var x = i.(int)
178+
9 println(x)
179+
10 }
180+
```
181+
182+
老规矩,看汇编知一切,主要是第 7 行:
183+
184+
```go
185+
0x0021 00033 (interface.go:7) MOVQ $999, (SP) // 把 999 作为参数挪到栈底
186+
0x0029 00041 (interface.go:7) CALL runtime.convT64(SB) // 以 999 作为参数调用 runtime.convT64,内部会为该变量在堆上分配额外空间,并返回其地址
187+
0x002e 00046 (interface.go:7) MOVQ 8(SP), AX // 返回值移动到 AX 寄存器
188+
0x0033 00051 (interface.go:7) LEAQ type.int(SB), CX // 将 type.int 值赋值给 CX 寄存器
189+
0x003a 00058 (interface.go:7) MOVQ CX, "".i(SB) // 将 type.int 赋值给 eface._type
190+
0x004a 00074 (interface.go:7) MOVQ AX, "".i+8(SB) // 将数据 999 的地址赋值给 eface.data
191+
```
192+
193+
还是比较简单的。
152194

153195
### eface 转换为普通类型
154196

197+
空接口转成普通类型,其实就是断言。我们继续使用上面的 demo:
198+
199+
```go
200+
1 package main
201+
2
202+
3 var i interface{}
203+
4
204+
5 func main() {
205+
6 var y = 999
206+
7 i = y
207+
8 var x = i.(int) // 这次关注断言的实现
208+
9 println(x)
209+
10 }
210+
```
211+
212+
看看第 8 行的输出:
213+
214+
```go
215+
0x0033 00051 (interface.go:7) LEAQ type.int(SB), CX // 在第 7 行把 type.int 搬到 CX 寄存器了,注意下面要用到
216+
.....
217+
0x0051 00081 (interface.go:8) MOVQ "".i+8(SB), AX // AX = eface.data
218+
0x0058 00088 (interface.go:8) MOVQ "".i(SB), DX // DX = eface._type
219+
0x005f 00095 (interface.go:8) CMPQ DX, CX // 比较 _type 和 type.int 是否相等
220+
0x0062 00098 (interface.go:8) JNE 161 // 如果不等,说明断言失败,跳到 161 位置,开始执行 panic 流程
221+
0x0064 00100 (interface.go:8) MOVQ (AX), AX // 把 AX 地址里的内容搬到 AX 寄存器,现在 AX 里存的是 999 了
222+
0x0067 00103 (interface.go:8) MOVQ AX, "".x+24(SP) // 将 999 赋值给变量 x
223+
224+
// 下面这部分是实现 panic 的,不是我们的重点
225+
0x00a1 00161 (interface.go:8) MOVQ DX, (SP)
226+
0x00a5 00165 (interface.go:8) MOVQ CX, 8(SP)
227+
0x00aa 00170 (interface.go:8) LEAQ type.interface {}(SB), AX
228+
0x00b1 00177 (interface.go:8) MOVQ AX, 16(SP)
229+
0x00b6 00182 (interface.go:8) CALL runtime.panicdottypeE(SB)
230+
0x00bb 00187 (interface.go:8) XCHGL AX, AX
231+
0x00bc 00188 (interface.go:8) NOP
232+
```
233+
234+
也挺简单的,这里我们用的是 int 来进行实验,对于稍微复杂一些的复合类型,也只是多出了一些步骤,本质上没有什么区别,感兴趣的读者可以自行研究。
235+
236+
### 普通类型转换为 iface
237+
155238
### iface 转换为普通类型
156239

157240
### 类型转换图

0 commit comments

Comments
 (0)