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 ?
for
loopfor 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.
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.
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.
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.
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 always1 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 [1].
(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.