t和* t之间的差异 [英] The difference between t and *t

查看:150
本文介绍了t和* t之间的差异的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  package main 

importfmt

type TT struct {
a int
b float32
c string

$ b 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 包会检查打印的值有一个 String()字符串方法(换句话说:如果它实现 fmt.Stringer 界面),如果是这样,它将被调用来获取字符串值的表示。



这在 fmt 包doc中有记录:


[...]如果一个操作数实现了方法String(),那么将调用该方法将该对象转换为一个字符串,然后按照动词的要求进行格式化(如果有的话)。


这里:

  return fmt.Sprintf(%+ v,* t)

您传递 * t 类型的 TT 添加到 fmt 包中。如果 TT.String()方法有一个指针接收器,那么<​​a href =https://golang.org/ref/spec#Method_sets = TT 不包含 String()方法集 c $ c>方法,所以 fmt 包不会调用它(只有 * TT 方法集包含它)。
$ b

如果您将接收器更改为非指针类型,则类型为 TT 将包含 String()方法,所以 fmt 包将会调用它,但是this是我们目前的方法,所以这是一个无止境的间接递归。



预防/保护



如果出于某种原因,您确实需要使用与传递给 fmt 包的值类型相同的接收方类型,避免这种/保护的一种简单而常用的方法从它是创建一个新的类型与 键入 关键字,并使用 conversion

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

试试去游乐场



但为什么这会起作用?因为类型关键字创建一个新类型,而新类型将有方法(它不会继承底层类型的方法)。



这是否会导致运行时间开销?不可以。从规范:类型声明引用:


特定规则适用于数字类型之间或字符串类型之间的(非常量)转换。这些转换可能会改变 x 的表示并导致运行时成本。 所有其他转换只会改变类型,但不会改变 x 的表示。

阅读更多关于此的信息: 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())
}

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

解决方案

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.

This is documented in the fmt package doc:

[...] 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).

Here:

return fmt.Sprintf("%+v", *t)

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

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".

Prevention / protection

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

Try this on the Go Playground.

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:

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.

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

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

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