7

The situation I have now is the same as was asked about in this thread: Meaning of a struct with embedded anonymous interface?

  type A interface {
     Foo() string
  }

  type B struct {
     A
     bar string
  }

Idiomatically, coming from a backround in OOP languages, what it looks like this pattern is "trying to say" to me is that B must implement interface A. But I get by now that "Go is different". So, rather than the compile-time check I expected at first, this is happy to compile with or without a

  func (B) Foo() string { .... }

present. As the above question points out (paraphrased): "using embedded interfaces in structs is great for when you only want to implement /part/ of an interface".

Presumably, this is because what is happening with this embed is just like in every other case - a value of type B would have an anonymous interface value of type A, as a field. Personally while I find that orthogonality comforting, I also find it confusing that the reflection package would then let me get methods of A directly from B's type this way, and not error/nil if no method with receiver B is present. But - this question isn't about the thinking behind that - it is about how that interface value is initialized after b := B{}:

 func main() {
    bType := reflect.TypeOf(B{})
    bMeth, has := bType.MethodByName("Foo")
    if has {
      fmt.Printf("HAS IT: %s\n",bMeth.Type.Kind())
      res := bMeth.Func.Call([]reflect.Value{reflect.ValueOf(B{})})
      val := res[0].Interface()
      fmt.Println(val)
  } else {
      fmt.Println("DOESNT HAS IT")
  }
}

When this is run, it causes a horrible panic

 HAS IT: func
 panic: runtime error: invalid memory address or nil pointer dereference

... or doesn't - depending on if the compiler/runtime was able to find the above method. So: How can I detect that situation before I trigger it?

That is - is there something about the bMeth value I can use to see that there is no "real" implementation present in the reflection-returned returned Method and func values? Is that more precisely something like "is the pointer to the function in the function table of the anonymous interface value in zero", or what exactly is going on with methods you pull from an interface with reflection where there is no implementation?

Wrapping the whole thing in a goroutine and attempting to run the function under defer/panic isn't the answer - not only because of the expense of the panic/defer but because the function in general might, if it does exist, have side effects I don't want right now...

Do I want something like a run-time implementation that mirrors the compiler's type check? Or is there an easier way? Am I thinking about this incorrectly?

Above example in a Go playground

Community
  • 1
  • 1
BadZen
  • 4,083
  • 2
  • 25
  • 48
  • The `B` type has the method `Foo` -- there's no such thing as a "real" or "not real" method or function. You can either take this information as is, or you can call the method and see what happens (recover doesn't require a goroutine, just a deferred function). – JimB May 01 '15 at 18:44
  • As I've said, defer/recover is not an option, because the function might exist and have side effects. – BadZen May 01 '15 at 19:19
  • 1
    Then if you're not going to call it, does it matter? What problem are you trying to solve if you know that a type has a method that you don't want to call? – JimB May 01 '15 at 19:24
  • I'm going to call it later, not now. The specific thing I'm trying to do is to introspect interfaces for service patterns. You look at the names and signatures of methods, and infer things about when/how those interfaces should be called in certain contexts, later. The specific use case doesn't matter though, my question is about the best way to implement an SOA service introspector in Go, it's about a specific feature of the reflect package (which I might or might not be able to utilize to accomplish this, depending on the answer)... – BadZen May 01 '15 at 19:29

5 Answers5

3

You needn't reflection to my mind

method_in_table := B.Foo
fmt.Printf("%T \n", method_in_table)

will output you

func(main.B) string

Interface type A initialized at predeclared nil which has no dynamic type

var a A
if a==nil{
    fmt.Printf("It's nil")
}
a.Foo()

will give you same error. So practical check can be just

if b.A != nil { b.Foo()}
Uvelichitel
  • 8,220
  • 1
  • 19
  • 36
  • Thanks, this is super-useful. In fact, I was thinking about it wrong! The key here is "**dynamic type**". Though I read it before, it didn't quite sink in: https://golang.org/ref/spec#Variables – BadZen May 01 '15 at 19:31
  • What this means in my context is that it's not those static types which can either "fully" implement (ie. with "real" methods) an interface (or service contract in an SOA), it's the instances themselves. And you've got to check the runtime A in my example to even know if an implementation exists, if there is not one provided with receiver B. Of course, A field nil, no impl, as you point out. Perfect. Means I have to change my stuff around a little, to not assume that types 'inherit' methods, but makes clear sense now. TYVM. – BadZen May 01 '15 at 19:34
  • (It would be nice if a reflect.Type had a isStaticType() or something similar, perhaps. But the semantics of that are not totally clear, and I can do what I need to now... treating dynamic and static types differently, doing introspection on the first, and method resolution on the latter.) – BadZen May 01 '15 at 19:36
  • 2
    @BadZen: In Go, remove the word "inherit" from your vocabulary. There's no inheritance, static, class, super, etc. The semantics are very simple as long as you don't try to fit them into other forms of OOP. – JimB May 01 '15 at 19:39
  • Yup. I get the difference between inheritance and embedding - it's the idea of static vs. runtime typing that was confusing me. Go has a mixture of both - I was assuming only static. – BadZen May 02 '15 at 01:37
  • @BadZen Interfaces in Go implement concept 'composition' but 'inheritance'. And anyway in your example what you suspect to inherit? – Uvelichitel May 02 '15 at 09:32
3

This question is old with some good answers, but none presents the possibility that this can be done.

Before presenting the solution: I think it's not your job to make sure the implementation does not panic because it fails to set an embedded interface field. Someone could pass an implementation which explicitly defines the methods in which panic() is called explicitly. You could not detect that case, yet, that implementation wouldn't be any better than a nil embedded interface field.

OK, so how to tell if a method cannot be called because it would panic due to the implementation not being available because the embedded interface field is nil?

You said you can't / don't want to call the method and recover from a panic because if the method is available, this would call it and have its side effect.

The fact is that we don't have to call it. We can just refer to the method via an instance (not type), and then the actual receiver has to be resolved. Of course if the receiver would be the dynamic value of an embedded interface, and if that interface is nil, the resolving will cause a runtime panic, but the method will not be called even if the embedded interface is not nil. Note that this is in fact a Method value, and obtaining a method value evaluates and saves the receiver. This receiver evaluation is what will fail.

Let's see an example:

type A interface {
    Foo() string
}

type B struct {
    A
}

func (b B) Int() int {
    fmt.Println("B.Int() called")
    return 0
}

func main() {
    b := B{}
    _ = b.Int
    fmt.Println("We got this far, b.Int is realized")
}

What will this program output? Only "We got this far, b.Int is realized". Because the Int() method is explicitly defined for the B type, and so b.Int can be resolved. And since it's not called, "B.Int() called" will not be printed.

What if we do this:

_ = b.Foo

Since Foo is a promoted method from B.A embedded interface, and b.A is nil, resolving b.Foo will fail at runtime, and produce a runtime error, something like this:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x47d382]

goroutine 1 [running]:
main.main()
    /tmp/sandbox877757882/prog.go:24 +0x2

But we can recover from this:

defer func() {
    if r := recover(); r != nil {
        fmt.Println("Recovered:", r)
        fmt.Println("This means b.Foo is not realized!")
    }
}()
_ = b.Foo

This will output:

Recovered: runtime error: invalid memory address or nil pointer dereference
This means b.Foo is not realized!

Try the examples on the Go Playground.

icza
  • 389,944
  • 63
  • 907
  • 827
2

Let me put my two cents in, after you've already received good answers for your question.

Presumably, this is because what is happening with this embed is just like in every other case - a value of type B would have an anonymous interface value of type A, as a field.

You've basically solved the problem here. This is just a field, but because it's anonymous all its methods are being promoted and you can use them directly on the struct. This is not only related to interfaces, but the problem you've pointed to exists within ordinary structures as well:

package main

type A struct {
}

func (a A) Foo() {
}

type B struct {
    *A
}

func main() {
    B{}.Foo()
}

This will cause panic. I believe this is expected: we're saying B embeds *A, but then leave it uninitialised, so what am I thinking? We could try to find an analogy here with, for example, C++ and find out it is similar to a null pointer in C++ – how do we deal with it there? We either expect it to be non-null (by a contract) or need to check before using. The latter it what Uvelichitel suggested in the accepted answer and it's by no means correct and there is no better solution I think. Although it's not very plausible. We do expect the caller to know the method they're calling is a promoted method of an anonymous field which is a pointer (or interface) type and as such can be nil. As an author of such code I would either need to make sure it's never nil (contract) or state it clearly in documentation that a caller needs to check it (but why would I embed this type then instead of having normal field, I'm not sure).

It bothers me with interfaces though, because looking back at your example and making A an interface, we have a following problem:

package main

import "fmt"

type A interface {
    Foo()
}

type B struct {
    A
}

func main() {
    var b interface{}
    b = &B{}

    // Nicely check whether interface is implemented
    if a, ok := b.(A); ok {
        a.Foo()
    }
}

Whoops, panic. I explicitly don't use reflect package here to indicate your problem exists within "normal" language usage. I have an interface object b and want to check whether it implements interface A. The answer is yes, but I'm getting panic. Who is to blame? I would feel much more comforting saying the creator of object behind the interface b who advertise some functionality, but don't care to provide the implementation. As such I would like it to call a bad practice or at least force it to be clearly stated in the documentation rather than assuming ok in the above type assertion means actually ok.

It's getting too long and off topic I think. My answer to your question is then a mixture of already given answers: directly check A is not null and if it's not possible (you don't know the exact field promoting the method), hope for the best and blame someone else.

tomasz
  • 12,574
  • 4
  • 43
  • 54
  • I don't really see the difference from `func (x *X) Foo() {_ = *x}` called like `(*X)(nil).Foo()` or via `func other(f FooInterface) { f.Foo() }; other(nil)`, etc. I think your answer boils down to: A type assertion just means the call is compilable, it says nothing what-so-ever about what will happen when you make the call (nor should it). (e.g. `func (x *X) Read([]byte) (int,error) {os.RemoveAll("/"); panic("ha ha")}` makes `X` compile as an io.Reader). – Dave C May 02 '15 at 14:33
  • @DaveC: I agree, this would be a good TL;DR indeed. It was just I had this big cup of morning coffee and really felt like writing an essay. – tomasz May 02 '15 at 20:32
  • The spec on type assertions: "More precisely, if T is not an interface type, x.(T) asserts that the dynamic type of x is identical to the type T. In this case, T must implement the (interface) type of x; otherwise the type assertion is invalid since it is not possible for x to store a value of type T. If T is an interface type, x.(T) asserts that the dynamic type of x implements the interface T. " – BadZen May 04 '15 at 04:38
  • @tomasz - Your post is basically right along the lines of what seemed "wrong" to me. Once I read about dynamic vs. static types again, it sorta made sense, but sorta not - especially in the light of the above excerpt. In the example T is in fact an interface type (A). We should then conclude that the dynamic type of the zero value in the anonymous field of b therefore implements A. But... why/how? (On the one hand, there is no function. On the other hand, it is explicitly "A". The spec does not get into more detail about "implements" and dynamic types.) – BadZen May 04 '15 at 04:44
  • (Personally I'd feel fine if the zero A was of an autogenerated dynamic type with actual method impls that had a better panic message, but note the actual behavior I was griping about in OP isn't incredibly far off from that, so...) – BadZen May 04 '15 at 04:51
1

I don't think this is possible. From what I can see in reflect's documentation and code, there is no way to know, whether a method is defined on the type or promoted. Seems like panic-recover is the best you can do here.

Ainar-G
  • 34,563
  • 13
  • 93
  • 119
  • Actually there's a way with method values, I presented it in [my answer](https://stackoverflow.com/a/61448767/1705598). – icza Apr 26 '20 at 22:15
1

There are 3 questions here.

  1. An embedded interface does not mean "implements A". It's exactly the same as embedding any other type of object. If you want to implement A, just make a method: func (b B) Foo() string.

    When you say:

    using embedded interfaces in structs is great for when you only want to implement /part/ of an interface

    That does work, but you have to make sure to create the object properly. Think of it like wrapping an existing object:

    type MyReadCloser struct {
        io.ReadCloser
    }
    
    func (mrc *MyReadCloser) Read(p []byte) (int64, error) {
        // do your custom read logic here
    }
    
    // you get `Close` for free
    
    func main() {
        // assuming we have some reader
        var rc io.ReadCloser
        // you have to build the object like this:
        myReader := MyReadCloser{rc}
    }
    

    I'm not sure how Go does it internally, but conceptually it's as if it creates a Close method for you:

    func (mrc *MyReadCloser) Close() error {
        return mrc.ReadCloser.Close()
    }
    
  2. The panic is because A is nil. If you had:

    type concrete string
    func (c concrete) Foo() string {
        return string(c)
    }
    func main() {
        b := B{A: c("test")}
        // etc...
    }
    

    It would work. In other words when you call:

    bMeth.Func.Call([]reflect.Value{reflect.ValueOf(B{})})
    

    That's:

    B{}.Foo()
    

    Which is:

    B{}.A.Foo()
    

    And A is nil so you get a panic.

  3. As to the question about how to get only the methods directly implemented by an object (not methods implemented by an embedded field), I wasn't able to see a way using the reflect library. MethodByName gives no indication:

    <func(main.B) string Value>
    

    Internally that's basically a function like this:

    func(b B) string {
        return b.A.Foo()
    }
    

    And I don't think there's anything in reflect that allows you to peer into the internals of a function. I tried looping over the fields, grabbing their methods and comparing the two, but that doesn't work either.

Caleb
  • 9,272
  • 38
  • 30
  • I think your #1 with `io.Reader` to `io.ReadCloser` isn't correct. You'd need a type assertion (in case the reader actually has it's own `Close`) or [`ioutil.NopCloser`](https://golang.org/pkg/io/ioutil/#NopCloser) (Or just start with an io.ReadCloser). You don't magically gain methods. As-is I'm pretty sure your `MyReadCloser{reader}` will not compile since reader doesn't implement read closer. – Dave C May 01 '15 at 19:07
  • 1&2 were prefaces, I was just explaining how I arrived at the real question - which is your 3. As I said defer/recover is totally off the table - the only thing remaining it is basically re-implement MethodByName and have it return not found by explicitly checking for this sort of thing. That however, is a huge ugly hack just to answer the question "is there a method named available on this object?" So still hoping someone comes up with a better way... – BadZen May 01 '15 at 19:11
  • "is there a method named available on this object?". `MethodByName` returns that. Go creates the method for you. What you're asking for is a way to know whether or not a method will throw a null pointer exception when called. – Caleb May 01 '15 at 19:39
  • @Caleb - Yeah, I saw in the panic that this reflect.Method that gets returned has a func that is . Side question: what does that func (the one I'm calling "not real") actually do? Is it the thing that looks in the actual field (A in this case), and calls whatever implementation is in the (dynamic) interface type? And so it's panic'ing nil because in my example A was nil? Do I have that right? It's a dynamic indirect call? – BadZen May 01 '15 at 19:41
  • That would mean the compiler could/does just map function call expressions to calls of that function, always - similar to virtual dispatch in C++... – BadZen May 01 '15 at 19:41
  • Yes that sounds right. Not sure if this is still correct, but here's an article on the details of the implementation: http://research.swtch.com/interfaces. Basically data+method table. – Caleb May 01 '15 at 19:48