常晓龙*_*常晓龙 1 stack-overflow string methods pointers go
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())
}
Run Code Online (Sandbox Code Playgroud)
代码可以很好地工作.但是,如果我String按照以下方式更改方法,则会导致死循环.不同之处在于*t被替换为t.为什么?
func (t *TT) String() string {
return fmt.Sprintf("%+v", t)
}
Run Code Online (Sandbox Code Playgroud)
因为fmt包检查正在打印的值是否有String() string方法(或者换句话说:如果它实现了fmt.Stringer接口),如果是,则调用它来获取string值的表示.
这包含在fmtdoc文档中:
[...]如果操作数实现方法String()字符串,则将调用该方法将对象转换为字符串,然后根据动词的需要对其进行格式化(如果有).
这里:
return fmt.Sprintf("%+v", *t)
Run Code Online (Sandbox Code Playgroud)
您正在将*t类型的值传递TT给fmt包.如果该TT.String()方法具有指针接收器,则该类型的方法集TT 不包括该String()方法,因此fmt包不会调用它(仅*TT包括它的方法集).
如果更改接收到非指针类型,那么该类型的方法集TT 将包括的String()方法,所以fmt包将调用,但是这是目前我们的方法,所以这是一个无休止的"间接递归".
如果由于某种原因你需要使用与传递给fmt包的值类型相同的接收器类型,一种简单而常见的方法来避免这种情况/保护它是type使用关键字创建一个新类型,并使用类型对传递的值的转换:
func (t TT) String() string {
type TT2 TT
return fmt.Sprintf("%+v", TT2(t))
}
Run Code Online (Sandbox Code Playgroud)
在Go Playground尝试这个.
但为什么这有效呢?因为type关键字创建了一个新类型,并且新类型将具有零方法(它不"继承"基础类型的方法).
特定规则适用于数字类型之间或字符串类型之间的(非常量)转换.这些转换可能会改变
x运行时成本并导致运行时成本.所有其他转换仅更改类型,但不更改表示x.
在这里阅读更多相关信息:Go中的别名类型之间的转换是否创建副本?