【问题标题】:Pointer to an interface in Go指向 Go 接口的指针
【发布时间】:2014-02-16 02:25:12
【问题描述】:

我目前正在阅读https://github.com/codegangsta/inject go 包的源代码,以了解该包是如何工作的。

我对 https://github.com/codegangsta/inject/blob/master/inject.go 文件有一些疑问,该文件使用了我不理解的 Go 语言的某些元素,并且在文档中找不到准确的解释。

// InterfaceOf dereferences a pointer to an Interface type.
// It panics if value is not an pointer to an interface.

func InterfaceOf(value interface{}) reflect.Type {
        t := reflect.TypeOf(value)

        for t.Kind() == reflect.Ptr {
                t = t.Elem()
        }

        if t.Kind() != reflect.Interface {
                panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)")
        }

        return t
}

我的第一个问题是关于for 循环。为什么它使用带有测试表达式的 for 循环?

第二个与恐慌函数中的消息有关。 (*MyInterface)(nil) 提到了“指向接口的指针”。当您检查类型是否实现结构时,我只会在 go 文档中遇到有关“编译时检查结构”的类似结构:

var _ SomeType = (*SomeInterface)(nil)

我没有找到关于带有(*Interface)(nil) 和接口指针的语句的任何信息。

我们应该如何解释这句话?与指向接口的指针有什么关系,我在哪里可以找到有关指向接口的指针的信息?

【问题讨论】:

  • 其他语言中的for与while循环相同
  • 好的!我对第一个问题太愚蠢了:p 对于第二个问题,你有像第一个问题一样清楚的解释吗?
  • 它是一种转换语法。所以将 nil 转换为 *MyInterface

标签: go


【解决方案1】:

总结bothanswers:

for 循环

for t.Kind() == reflect.Ptr {
    t = t.Elem()
}

t.Elem() 是等价于*t 的反射,所以只要它持有另一个指针值,这个循环就会解除对t 的引用。在循环结束时,t 将保存最后一个指针指向的值,不再是指针。

消息

调用 [...],其值不是指向接口的指针。 (*MyInterface)(nil)

表达式(*MyInterface)(nil) 只是预期作为参数的一个(措辞不佳)示例。

语法是conversion。在这种情况下,转换将尝试将值(在本例中为 nil)转换为给定类型(*MyInterface)。所以,

(*MyInterface)(nil) 

会给你一个*MyInterface 的零值,它的接口类型是MyInterface (play):

x := (*MyInterface)(nil)
InterfaceOf(x) // MyInterface

当然,这个值并没有指向有意义的地方。

接口实现的编译时检查

为避免混淆,您展示的构造

var _ SomeType = (*SomeInterface)(nil)

可能不是您想要的。我猜你想要这个:

var _ SomeInterface = (*SomeType)(nil)

此构造允许对某些类型的接口实现进行编译时检查。 因此,如果您正在编写某种类型的库,并且想要满足一个没有 使用它,您可以使用它来确保您的结构实现了接口。

为什么会这样

首先,var _ someType 是编译器将要检查的变量,但是 由于Blank Identifier _,将不在编译程序中且不可访问:

空白标识符可以像声明中的任何其他标识符一样使用,但它不会引入绑定,因此不会被声明。

这使您可以声明任意数量的这些构造,而不会干扰 程序的其余部分。

您可以通过以下方式声明任何类型的指针的零值:

(*T)(nil)

检查this example on play。

接下来,assignability 表示如果T 是一个接口并且x 实现T,则x 可以分配给T。

总结一下:

T _ = (*x)(nil)

强制x 实现T,因为其他一切都会出错。

【讨论】:

    【解决方案2】:

    for 循环用于不断取消引用类型,直到它不再是指针。这将处理类型获得额外间接的情况。
    例如play.golang.org/p/vR2gKNJChE

    至于(*MyInterface)(nil),指向接口的指针总是1一个错误的Go代码。我假设作者只是在描述他通过指向与代码 sn-p 接口的指针的含义,因为它们非常罕见。

    如果你仍然对被禁止的类型感兴趣,Russ Cox 有一些信息,这一切到底是如何运作的:research.swtch.com/interfaces。您将很难找到有关使用指向接口的指针的信息,因为 [1]。

    (1) 好的,并非总是如此,但老实说,除非您是围棋专家,否则不要这样做。在这种情况下,不要告诉任何人。

    【讨论】:

    • 公式“禁止类型”、“Go pro”和指向接口的指针的泛化总是错误代码往往比回答问题更神秘。这并不是真正必要的,而且这一切都不是很神奇。请考虑改写您的答案,因为取消引用部分是正确的。我会赞成这个答案。
    • @nemo:应该如何回答“与指向接口的指针有什么关系,我在哪里可以找到有关指向接口的指针的信息?”我想在不造成伤害的情况下给他一些东西。
    • 可能造成什么损害?如果问题仍然存在,可以提出并回答。尽量解释清楚?
    【解决方案3】:

    for 循环与其他语言中的while 循环相同

    第二件事只是转换的语法:

    (*Point)(p)      // p is converted to *Point
    

    因为这个库是如何工作的,你只需将指针传递给接口,for循环然后取消引用它(如果我们传递类似 (***MyInterface)(nil) 的东西),然后 if 语句检查 ty[e 指向to 是一个接口。

    【讨论】:

      猜你喜欢
      • 2019-07-07
      • 1970-01-01
      • 1970-01-01
      • 2016-07-24
      • 2016-07-17
      • 2016-07-12
      • 2018-11-06
      • 1970-01-01
      • 2013-09-08
      相关资源
      最近更新 更多