【问题标题】:Confused with implicit pointer dereference when assigning a pointer to interface in Go在 Go 中为接口分配指针时与隐式指针取消引用混淆
【发布时间】:2015-09-12 23:29:45
【问题描述】:

我是 Go 新手,正在研究它的界面功能。

代码如下:

package main

import (
        "fmt"
        "reflect"
)

type Integer int

func (a Integer) Less(b Integer) bool {
        return a < b
}

func (a *Integer) Add(b Integer) {
        *a += b
}

type LessAdder interface {
        Less(b Integer) bool
        Add(b Integer)
}

var a Integer = 1

var b LessAdder = &a

func main() {
        fmt.Println(reflect.TypeOf(b))
        fmt.Println(b.Less(2))
        b.Add(a)
        fmt.Println(a)

}

它会输出以下内容:

*main.Integer
true
2

嗯,这很好用。

重点是: var b LessAdder = &amp;a 是如何工作的。指针自动解引用发生在此处,还是在 b 调用成员方法时发生?

输出 *main.Integer 告诉我们 b 是指向类型 Integer 的指针,因此是第二种情况。

然后棘手的事情来了: 当我在代码中添加fmt.Pringln(*b) 时,编译器出现错误:

 demo/demo1
./demo1.go:31: invalid indirect of b (type LessAdder)

这让我很困惑。 由于b 是指向Integer 的指针类型,因此取消引用它应该可以工作。但为什么不呢?

【问题讨论】:

  • 除了 icza 所说的:接口是 Go 中的 类型,它们不是语法糖或神奇的包装器。接口类型与 int32 或用户定义的结构等内置类型具有相同的状态。

标签: pointers go interface


【解决方案1】:

你的最后一句话:

“由于b 是指向Integer 的指针类型,那么取消引用它应该可以工作。”

停在那里。 b 不是指针类型的变量,因此您不能 dereference 它。

它是interface type的一个变量,它示意性地是一对值和一个类型(值,类型),持有&amp;a作为值和*Integer作为类型(博客文章The Laws of Reflection,部分The representation of an interface)。

这是一个指针类型变量的声明,*Integer

var ip *Integer

这是一种接口类型:

var intf LessAdder

当你这样做时:

var b LessAdder = &a

发生的情况是接口值(LessAdder 类型)被自动/隐式创建,它将保存值&amp;a(和类型*Integer)。这是一个有效的操作,因为&amp;a 的类型(即*Integer)实现了接口LessAdder*Integermethod set 是接口LessAdder 的超集(在这种情况下它们是相等的) , 接口类型的方法集就是它的接口)。

现在当您调用b.Less(2) 时,由于Less() 有一个值接收器,指针将被取消引用,并且将生成指向值的副本并将其用作/传递作为方法Less() 的值接收器。

fmt.Println(reflect.TypeOf(b)) 不会说谎,但它会打印出b动态 类型。 b 的动态类型确实是*Integer,但是bstatic 类型是LessAdder,而静态类型决定了你可以用一个值做什么以及什么运算符或方法是允许的。

【讨论】:

  • 那么bInterface{&amp;a, *Integer}的一个类型,而Interface是一个类型的类型?
  • @LeckieNi 从原理上讲,是的。但是会发生一个接口值被自动/隐式地创建并持有(&amp;a, *Integer)这对,并且这个接口值被分配给b。请参阅编辑后的答案。
  • 然后b调用b.Less(2),其实是调用(&amp;a).Less(2)。这里是自动取消引用发生的地方?
  • @LeckieNi b 持有一个指针值,类型为*Integer。由于Less() 有值接收者,这个指针将被取消引用,并且将制作指向值的副本并将其用作/传递作为方法的值接收者。由于b 拥有一个指针&amp;a,因此将使用a 的值。请参阅编辑后的答案。
  • 那么,fmt.Println(reflect.TypeOf(b)) 的输出在说谎并且打印错误类型?
【解决方案2】:

LessAdder 被声明为带有方法LessAdd 的接口。由于Add 是用*Integer 的接收者声明的,所以*Integer 可以是LessAdderInteger 不能。当您执行var b LessAdder = &amp;a 时,指向a 的指针存储在接口b 中。

自动间接发生在调用b.Less(2) 时,因为*Integer 上的方法和Integer 上的方法都对*Integer 的方法集有贡献。

您不能使用*b,因为尽管b 包含 *Integer,但它的静态类型是LessAdder,而不是*Integer。撇开接口的表示不谈,LessAdder 不是指针类型,而*b,如果允许的话,根本就没有可表达的类型。

您可以使用type assertion 再次以Integer * 的身份访问bb.(*Integer)*Integer 类型的表达式,*b.(*Integer)Integer。如果b 中的值毕竟不是*Integer,那么这两者都会在运行时出现恐慌。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多