Go基础学习 fmt.Stringer接口的坑

一个隐含实现导致的问题:

由于Go的interface的实现是隐含的,只要函数一致就会自动匹配。
先看一下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

type People struct {
    Name string
}

func (p *People) String() string {
    return fmt.Sprintf("你好: %v", p)
}

func main() {
    p := &People{}
    p.String()
}

上面的代码运行结果:

1
2
3
4
5
runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

runtime stack:
runtime.throw(0x10c122b, 0xe)


原因分析:

fmt包中有如下接口:

1
2
3
type Stringer interface {
    String() string
}

调用fmt.Sprintf函数时,会先查找传入的对象是否可以用该接口输出。
由于上面的代码意外实现了Stringer接口,所以会形成无限递归循环,导致栈溢出。
解决方法就是在实现String()函数时把内部成员取出打印。

1
2
3
4
...
    fmt.Sprintf("print: %v", p.Name)
    fmt.Sprintf("print: %v", string(p))	// 强制转换为string类型,也可以避免递归
...


如何避免?

  • 没什么办法,Go就是这样设计的,鱼与熊掌不可得兼…
  • 积累经验,在调用包时注意是否实现了隐含接口