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
。为什么呢?
func (t *TT) String() string {
return fmt.Sprintf("%+v", t)
}
答案 0 :(得分:5)
因为fmt
包检查正在打印的值是否具有String() string
方法(或者换句话说:如果它实现了fmt.Stringer
接口),如果是,则它将是调用以获取值的string
表示。
fmt
包doc:
[...]如果操作数实现方法String()字符串,则将调用该方法将对象转换为字符串,然后根据动词的需要对其进行格式化(如果有的话)。
下面:
return fmt.Sprintf("%+v", *t)
您正在将*t
类型的值TT
传递给fmt
包。如果TT.String()
方法具有指针接收器,则TT
类型的method set不包含 String()
方法,因此{{ 1}}包不会调用它(只有fmt
的方法集包含它)。
如果将接收器更改为非指针类型,则*TT
类型的方法集将包括 TT
方法,因此String()
package会调用它,但这是我们目前使用的方法,所以这是一个无休止的“间接递归”。
如果出于某种原因,您确实需要使用与传递给fmt
包的值类型相同的接收器类型,一种简单而常见的方法来避免这种情况/保护它是创建一个新的使用type
关键字键入,并对传递的值使用类型conversion:
fmt
在Go Playground上尝试此操作。
但为什么这样呢?因为func (t TT) String() string {
type TT2 TT
return fmt.Sprintf("%+v", TT2(t))
}
关键字会创建一个新类型,而新类型会有零方法(它不会“继承”基础类型的方法)。
这会产生一些运行时开销吗?不。引自Spec: Type declarations:
特定规则适用于数字类型之间的(非常量)转换或字符串类型之间的转换。这些转化可能会更改
type
的表示形式并产生运行时费用。 所有其他转化仅更改类型,但不更改x
的表示。
在此处详细了解:Does convertion between alias types in Go create copies?