Go中的接口指针

huangapple go评论68阅读模式
英文:

Pointer to an interface in Go

问题

我正在阅读https://github.com/codegangsta/inject Go包的源代码,以了解该包的工作原理。

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

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

第二个问题与panic函数中的消息有关。提到了“指向接口的指针”,并且使用了(*MyInterface)(nil)。我只在Go文档中遇到过类似的结构,用于检查类型是否实现了某个接口:

var _ SomeType = (*SomeInterface)(nil)

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

我们应该如何解释这个语句?指向接口的指针与之有何关系?在哪里可以找到关于指向接口的指针的信息?

英文:

I'm currently reading the source code of the https://github.com/codegangsta/inject go package to understand how does this package works.

I have some questions concerning the file https://github.com/codegangsta/inject/blob/master/inject.go file thats use some element of the Go language I don't understand and don't find precise explanations in the documentation.

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

My first question is concerning the for loop. Why does it uses a for loop with a test expression ?

The second relates to the message in the panic function. "A pointer to an interface" is mentioned with the (*MyInterface)(nil). I only encounter a similar construction in the go documentation concerning 'compile time checking structure' when you check that a type implements a structure :

var _ SomeType = (*SomeInterface)(nil)

I did not find any informations about a statement with (*Interface)(nil) and pointer to interface.

How should we interpret this statement ? What is the relation with a pointer to interface and where could I find informations about pointer to interface ?

答案1

得分: 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,否则会报错。

英文:

To summarize both answers:

The for loop

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

t.Elem() is the reflection equivalent to *t, so what this loop does it dereferencing t as long as it holds another pointer value. At the end of the loop, t will hold the value that the last pointer pointed to, not a pointer anymore.

The message

> Called [...] with a value that is not a pointer to an interface. (*MyInterface)(nil)

The expression (*MyInterface)(nil) is just an (poorly phrased) example of what is expected as parameter.

The syntax is that of a conversion. A conversion will attempt to convert a value (in this case nil) to a given type (*MyInterface) in this case. So,

(*MyInterface)(nil) 

will give you a zero value of a *MyInterface whose interface type would be MyInterface (play):

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

Of course, this value does not point somewhere meaningful.

Compile time checking of interface implementation

To avoid confusion, the construct you showed

var _ SomeType = (*SomeInterface)(nil)

is probably not what you wanted. I guess you wanted this:

var _ SomeInterface = (*SomeType)(nil)

This construct enables compile time checking of interface implementation for certain types.
So in case you're writing a library of some sort and you want to satisfy an interface without
using it, you can use this to make sure that your struct implements the interface.

Why this works

First of all, var _ someType is a variable that is going to be checked by the compiler but
will not be in the compiled program and is not accessible due to the Blank Identifier _:

> The blank identifier may be used like any other identifier in a declaration, but it does not introduce a binding and thus is not declared.

This enables you do declare an arbitrary number of these constructs without interfering with the
rest of the program.

You can declare a zero value of a pointer of any type by writing:

(*T)(nil)

Check this example on play.

Next, assignability says that x is assignable to T if T is an interface and x implements T.

So to summarize:

T _ = (*x)(nil)

enforces that x implements T as everything else would be an error.

答案2

得分: 1

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

至于(*MyInterface)(nil),指向接口的指针总是会导致错误的Go代码。我猜作者只是用代码片段描述了他对指向接口的指针的理解,因为这种情况非常罕见。

如果你对被禁止的类型还感兴趣,Russ Cox在这方面有一些关于内部工作原理的信息:research.swtch.com/interfaces。你会很难找到关于指向接口的指针使用的信息,因为……(未完整提供)。

(1)好吧,实际上并不总是这样,但老实说,除非你是Go专家,否则不要这样做。如果你是专家,请不要告诉任何人。

英文:

The for loop is used to continually dereference the type until it is no longer a pointer. This will handle case where the type acquired an extra indirection(s).
e.g. play.golang.org/p/vR2gKNJChE

As for the (*MyInterface)(nil), pointers to interfaces always<sup>1</sup> an error Go code. I assume the author is just describing what he means by pointer to interface with a code snippet since they are so rare.

If you're still intrigued by the forbidden type Russ Cox has some info how exactly all this works under the hood: research.swtch.com/interfaces. You'll have a hard time finding info on the use of pointers to an interface because [<sup>1</sup>].

(1) OK not really always, but honestly don't do it unless you're a Go pro. In which case don't tell anyone about it.

答案3

得分: 0

那个for循环与其他语言中的while循环是相同的。

第二个事情只是一个转换的语法:

(*Point)(p)      // p 被转换为 *Point

因为这个库的工作方式是,你只需要传递指向接口的指针,for循环会对其进行解引用(如果我们传递类似于(***MyInterface)(nil)的东西),然后if语句检查指向的类型是否是一个接口。

英文:

That for loop is identical to while loop in other languages

The second thing is just a syntax for conversions:

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

Because how this library works you just have to pass the pointer to interface, for loop then dereferences it (if we pass something like (***MyInterface)(nil)) and then if statement checks if the ty[e pointed to is an interface.

huangapple
  • 本文由 发表于 2014年1月25日 06:36:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/21343746.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定