package main
import "fmt" type TT struct { a int b float32 c string } func (t *TT) String() string { return fmt.Sprintf("%+v", *t) } func main() { tt := &TT{3, 4, "5"} fmt.Printf(tt.String()) }
该代码可以很好地工作。但是,如果String按如下所示更改方法,则会导致死循环。区别在于将*t替换为t。为什么?
String
*t
t
func (t *TT) String() string { return fmt.Sprintf("%+v", t) }
因为fmt程序包检查要打印的值是否具有String() string方法(或换句话说:是否实现fmt.Stringer接口),如果是,则将调用它以获取string值的表示形式。
fmt
String() string
fmt.Stringer
string
fmt软件包doc中对此进行了说明:
[…]如果操作数实现String()字符串方法,则将调用该方法将对象转换为字符串,然后根据动词的要求对其进行格式化(如果有)。
这里:
return fmt.Sprintf("%+v", *t)
您正在将*t类型的值传递TT给fmt包。如果该TT.String()方法具有指针接收器,则该类型的方法集TT 不包含 该String()方法,因此fmt程序包将不会调用它(仅*TT包含它的方法集)。
TT
TT.String()
String()
*TT
如果更改接收到非指针类型,那么该类型的方法集TT 将包括 的String()方法,所以fmt包将调用,但这是我们目前正处在的方法,所以这是一个无休止的“间接递归”。
如果由于某种原因您确实需要使用与传递给fmt包的值的类型相同的接收器类型,避免这种情况/避免这种情况发生的一种简单而通用的方法是type使用关键字创建一个新类型,并使用type 传递的值的转换:
type
func (t TT) String() string { type TT2 TT return fmt.Sprintf("%+v", TT2(t)) }
在Go Playground上尝试一下。
但是为什么这样做呢?因为type关键字创建了一个新类型,并且该新类型将具有 零个 方法(它不会“继承”基础类型的方法)。
这会产生一些运行时开销吗?编号。引用自规范:类型声明:
特定规则适用于数字类型之间或字符串类型之间的(非恒定)转换。这些转换可能会更改的表示形式x并产生运行时成本。 所有其他转换只会更改类型,而不会更改的表示形式x。
x