3

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 ?

yageek
  • 4,115
  • 3
  • 30
  • 48
  • that for is identical to while loop in other languages – Arjan Jan 24 '14 at 22:49
  • Ok! So stupid I am for the first question :p For the second one, do you have a explanation as clear as the first one ? – yageek Jan 24 '14 at 22:54
  • Its a syntax for conversions. so convert nil to *MyInterface – Arjan Jan 24 '14 at 23:13
  • Does this answer your question? [Ensure a type implements an interface at compile time in Go](https://stackoverflow.com/a/60663003) –  Mar 13 '20 at 03:48

3 Answers3

5

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.

Community
  • 1
  • 1
nemo
  • 55,207
  • 13
  • 135
  • 135
1

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.

deft_code
  • 57,255
  • 29
  • 141
  • 224
  • The formulations 'forbidden type', 'Go pro' and the generalization of pointers to interfaces being always error codes tend to mystify more than answer the question. This is not really necessary and this is all not very magical. Please consider rephrasing your answer as the dereferencing part is correct. I would've upvoted the answer. – nemo Jan 25 '14 at 02:59
  • @nemo: How should one answer "What is the relation with a pointer to interface and where could I find informations about pointer to interface?" I'd like to give him something without doing damage. – deft_code Jan 26 '14 at 05:05
  • what damage could possibly be done? If questions remain they can be asked and answered. Just try and explain as good as you can? – nemo Jan 26 '14 at 14:23
0

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.

Arjan
  • 19,957
  • 2
  • 55
  • 48