Go:fmt.Print系列函数为什么默认情况下不打印结构体中指针字段指向的值?

Go 专栏收录该内容
2 篇文章 0 订阅

在Go中,一个结构体如果含有一个指针字段,在默认情况下,使用fmt.Print系列函数打印这个结构体时,并不会打印这个指针指向的值。因此,对于下面的代码:

type Foo struct {
    A int
}
type Bar struct {
    F *Foo
}
func main() {
    b := Bar{&Foo{1}}
    fmt.Printf("%+v\n", b)
}

其输出为:

{F:0xc0000140a0}

map也是类似的,如果map的value类型为指针,fmt.Print系列函数同样不会打印指针指向的值。

如果想要把指针的值打印出来,那么这个指针指向的类型需要实现fmt.Stringer接口:

type Stringer interface {
    String() string
}

对于下面的代码:

type Foo struct {
    A int
}
func (f *Foo) String() string {
    return fmt.Sprintf("{A: %v}", f.A)
}
type Bar struct {
    F *Foo
}
func main() {
    b := Bar{&Foo{1}}
    fmt.Printf("%+v\n", b)
}

其输出为:

{F:{A: 1}}

为什么默认不打印指针字段指向的值?是因为Go的反射机制有什么限制吗?并不是,查看Go SDK的源码,我们会发现fmt.Print在打印指针时有一段特别的逻辑:fmt.Print只打印直接传入的指针指向的值,且这个值的类型必须是数组/切片/结构体/map。在print.go的printValue函数中,有这样一段代码:

    case reflect.Ptr:
        // pointer to array or slice or struct? ok at top level
        // but not embedded (avoid loops)
        if depth == 0 && f.Pointer() != 0 {
            switch a := f.Elem(); a.Kind() {
            case reflect.Array, reflect.Slice, reflect.Struct, reflect.Map:
                p.buf.writeByte('&')
                p.printValue(a, verb, depth+1)
                return
            }
        }
        fallthrough

其中depth是当前正在打印的值的层次,对于直接传入fmt.Print的值,depth设为0。如果传入fmt.Print的值是一个结构体,Go会尝试遍历打印结构体中所有的字段,在打印这些字段时depth会被设为1。depth为1的字段也可能是个结构体,结构体中还有字段,依次类推,depth还可以是2、3、4……如果一个要打印的值类型为指针,只有depth==0时才会尝试打印指针指向的值。至于为什么有这样的逻辑,代码中的注释写的很清楚了:为了避免陷入循环。试想一下,如果没有这样的逻辑限制,在打印下面的b时会发生什么事情呢?

type Baz struct {
    i interface{}
}
func main() {
    b := Baz{}
    b.i = &b
    fmt.Printf("%+v\n", b)
}
  • 1
    点赞
  • 0
    评论
  • 0
    收藏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值