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。为什么?

func (t *TT) String() string {
    return fmt.Sprintf("%+v", t)
}

因为 fmt package checks if the value being printed has a String() string method (or in other words: if it implements the fmt.Stringer 接口),如果是这样,它将被调用以获取值的 string 表示。

这在 fmt 包文档中有记录:

[...] 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类型的method set不包含String()方法,所以fmt 包不会调用它(只有 *TT 的方法集包含它)。

如果把接收者改成非指针类型,那么TT类型的方法集会包含String()方法,所以fmt 包会调用它,但这是我们目前使用的方法,所以这是一个无穷无尽的 "indirect recursion".

预防/保护

如果出于某种原因您确实需要使用与传递给 fmt 包的值类型相同的接收器类型,避免这种情况/保护它的一种简单而常用的方法是创建传递的值带有 type keyword, and use type conversion 的新类型:

func (t TT) String() string {
    type TT2 TT
    return fmt.Sprintf("%+v", TT2(t))
}

Go Playground 上试试这个。

但这为什么有效?因为 type 关键字创建了一个新类型,并且新类型将具有 个方法(它没有 "inherit" 基础类型的方法)。

这会产生一些 运行 时间的开销吗?编号引用自 Spec: Type declarations:

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 of x.

在此处阅读更多相关信息: