Go中的接口指针

3
我正在阅读 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循环?

第二个问题与panic函数中的消息有关。其中提到了一个接口指针(*MyInterface)(nil)。我只在go文档中遇到过类似的结构,用于检查类型是否实现一个结构体。

var _ SomeType = (*SomeInterface)(nil)

我没有找到关于(*Interface)(nil)和接口指针的任何信息。
我们应该如何解释这个语句?指针与接口有什么关系,我在哪里可以找到关于接口指针的信息?

for循环在其他语言中与while循环相同。 - Arjan
好的!我太蠢了,第一个问题:p。对于第二个问题,你有没有像第一个问题那样清晰的解释? - yageek
这是一种用于转换的语法。因此将nil转换为*MyInterface。 - Arjan
这个回答解决了你的问题吗?在Go中编译时确保类型实现接口 - user12817546
3个回答

5

总结两个答案:

for循环

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

t.Elem()是反射中与*t相当的表示方式,因此此循环所做的就是对t进行解引用,只要它仍然保存另一个指针值。在循环结束时,t将保存最后一个指针指向的值,不再是一个指针。

这个信息的含义

调用[...]时传递了一个不是接口指针的值。(*MyInterface)(nil)

(*MyInterface)(nil)表达式只是一个(措辞不太准确的)示例,说明了期望的参数。

语法是一种转换。在此情况下,转换将尝试将值(在此为nil)转换为给定类型(*MyInterface)。因此,

(*MyInterface)(nil) 

将会给你一个值为零的*MyInterface,其接口类型将是MyInterfaceplay):

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

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

接口实现的编译时检查

为了避免混淆,你展示的结构

var _ SomeType = (*SomeInterface)(nil)

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

var _ SomeInterface = (*SomeType)(nil)

这个结构允许在编译时检查某些类型的接口实现。因此,如果您正在编写某种库并想要满足接口而不使用它,则可以使用此功能来确保您的结构实现了接口。

为什么这有效

首先,var _ someType 是一个变量,将由编译器检查,但不会出现在编译后的程序中,并且由于空白标识符 _而无法访问:

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

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

您可以通过编写以下内容来声明任何类型的指针的零值:

(*T)(nil)

请在这个示例中进行检查。

接下来,可赋值性表示如果T是一个接口且x实现了T,那么x就可以被赋给T

因此,总结一下:

T _ = (*x)(nil)

强制实现 x 作为 T,否则会出现错误。


1
for 循环用于不断取消引用类型,直到它不再是指针。这将处理类型获取额外间接性的情况。
例如: play.golang.org/p/vR2gKNJChE 至于 (*MyInterface)(nil),接口指针总是会在 Go 代码中出错。我认为作者只是通过代码片段描述了他所说的接口指针,因为它们很少见。
如果您仍然对禁止类型感兴趣,Russ Cox 在此方面如何工作有一些信息: research.swtch.com/interfaces。 您将很难找到关于使用接口指针的信息,因为 [1]。 (1) 好吧,其实并不总是,但老实说,除非你是 Go 专家,请不要这样做。在这种情况下,不要告诉任何人。

“禁止类型”、“Go pro”和将指针泛化为接口总是错误码的说法往往更加神秘,而不是回答问题。这真的不是必要的,这一切都不是很神奇。请重新构思你的回答,因为解引用部分是正确的。我本来会点赞这个答案的。 - nemo
@nemo:如何回答“指向接口的指针有什么关系,我从哪里可以找到关于指向接口的指针的信息?”我想给他一些东西,而不会造成损害。 - deft_code
可能会造成什么损害?如果还有疑问,可以提出并回答。尽量好好解释一下吧? - nemo

0
那个 for 循环和其他语言中的 while 循环是一样的。
第二件事只是一个用于转换的语法:
(*Point)(p)      // p is converted to *Point

由于这个库的工作方式,您只需要传递指向接口的指针,然后循环解引用它(如果我们传递类似(***MyInterface)(nil)的东西),然后if语句检查所指向的类型是否为接口。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接