t和*t之间的差异

常晓龙*_*常晓龙 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)

icz*_*cza 5

因为fmt包检查正在打印的值是否有String() string方法(或者换句话说:如果它实现了fmt.Stringer接口),如果是,则调用它来获取string值的表示.

这包含在fmtdoc文档中:

[...]如果操作数实现方法String()字符串,则将调用该方法将对象转换为字符串,然后根据动词的需要对其进行格式化(如果有).

这里:

return fmt.Sprintf("%+v", *t)
Run Code Online (Sandbox Code Playgroud)

您正在将*t类型的值传递TTfmt包.如果该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中的别名类型之间的转换是否创建副本?