t 和 *t 的区别 [英] The difference between t and *t

查看:28
本文介绍了t 和 *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 of x.

在此处阅读更多相关信息:是否Go中别名类型之间的转换创建副本?

Read more about this here: Does convertion between alias types in Go create copies?

这篇关于t 和 *t 的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆