t 和 *t 的区别 [英] The difference between t and *t
问题描述
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
.为什么?
The code can work well. But if I change the String
method as in the following, it will cause dead loop. The difference is that the *t
is replaced with t
. Why?
func (t *TT) String() string {
return fmt.Sprintf("%+v", t)
}
推荐答案
因为fmt
包检查正在打印的值是否具有 String() string
方法(或者换句话说:它是否实现了 fmt.Stringer
接口),如果有则调用获取string
值的表示.
Because the fmt
package checks if the value being printed has a String() string
method (or in other words: if it implements the fmt.Stringer
interface), and if so, it will be called to get the string
representation of the value.
这记录在 fmt
包文档中:
This is documented in the fmt
package doc:
[...] 如果操作数实现了 String() 方法,则将调用该方法将对象转换为字符串,然后根据动词(如果有)的要求对其进行格式化.
[...] If an operand implements method String() string, that method will be invoked to convert the object to a string, which will then be formatted as required by the verb (if any).
这里:
return fmt.Sprintf("%+v", *t)
您正在将 TT
类型的值 *t
传递给 fmt
包.如果 TT.String()
方法有一个指针接收器,那么 方法集 类型的 TT
不包括 String()
方法,所以 fmt
包将不调用它(只有*TT
的方法集包含它).
You are passing a value *t
of type TT
to the fmt
package. If the TT.String()
method has a pointer receiver, then the method set of the type TT
does not include the String()
method, so the fmt
package will not call it (only the method set of *TT
includes it).
如果你将接收者改为非指针类型,那么TT
类型的方法集将包含String()
方法,所以 fmt
包会调用它,但这是我们当前使用的方法,所以这是一个无休止的间接递归".
If you change the receiver to non-pointer type, then the method set of the type TT
will include the String()
method, so the fmt
package will call that, but this is the method we're currently in, so that's an endless "indirect recursion".
如果出于某种原因,您确实需要使用与传递给 fmt
包的值的类型相同的接收器类型,则避免这种情况/保护的一种简单而常用的方法是使用 type
关键字创建一个新类型,并使用 type <传递的值的 href="https://golang.org/ref/spec#Conversions" rel="nofollow noreferrer">conversion:
If for some reason you do need to use the same receiver type as the type of the value you pass to the fmt
package, an easy and common way to avoid this / protect from it is to create a new type with the type
keyword, and use type conversion on the value being passed:
func (t TT) String() string {
type TT2 TT
return fmt.Sprintf("%+v", TT2(t))
}
在 Go Playground 上试试这个.
但是为什么会这样呢?因为type
关键字创建了一个新类型,而新类型将有零 个方法(它不会继承"底层类型的方法).
But why does this work? Because the type
keyword creates a new type, and the new type will have zero methods (it does not "inherit" the methods of the underlying type).
这是否会产生一些运行时开销?不.引用自 规范:类型声明:
Does this incur some run-time overhead? No. Quoting from Spec: Type declarations:
特定规则适用于数字类型之间或字符串类型之间的(非常量)转换.这些转换可能会改变 x
的表示并产生运行时成本.所有其他转换只改变了x
的类型而不是表示.
Specific rules apply to (non-constant) conversions between numeric types or to and from a string type. These conversions may change the representation of
x
and incur a run-time cost. All other conversions only change the type but not the representation ofx
.
在此处阅读更多相关信息:是否Go中别名类型之间的转换创建副本?
Read more about this here: Does convertion between alias types in Go create copies?
这篇关于t 和 *t 的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!