t和* t之间的差异

时间:2017-03-28 09:33:10

标签: string pointers go methods stack-overflow

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)
}

1 个答案:

答案 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?