3

So I'm basically trying to find out the best way to get to something like this:

package main

import "fmt"

type SomeStruct struct {
}

type SomeInterface interface {
  SomeMethodWhichNeedsAPointerReceiver() string
}

func (s *SomeStruct) SomeMethodWhichNeedsAPointerReceiver() string {
  return "Well, believe me, I wrote something"
}

func Run(s interface{}) {
  // how can I cast s to a pointer here?
  casted, ok := (s).(SomeInterface)
  if ok {
    fmt.Println("Awesome: " + casted.SomeMethodWhichNeedsAPointerReceiver())
    return 
  }

  fmt.Println("Fail :(")
}

func SomeThirdPartyMethod() interface{} {
  return SomeStruct{}
}

func main() {
  x := SomeThirdPartyMethod()
  Run(x)
}

My problem here is, at the typecast in the Run method. I basically only know that it is of type interface{} and now I need to call an interface method, which has a pointer receiver.

My only solution at the moment is to dynamically construct a slice with reflection, set the element into the slice, and than make it adressable.

Is this really the only possibility to find a solution?

Play link

icza
  • 389,944
  • 63
  • 907
  • 827
sharpner
  • 3,857
  • 3
  • 19
  • 28
  • possible duplicate of [Dynamically call method on interface{} regardless of receiver type](http://stackoverflow.com/questions/14116840/dynamically-call-method-on-interface-regardless-of-receiver-type) –  Apr 20 '15 at 10:14
  • well not really, since I actually want a solution without reflection. But yeah, if it isn't possible, it might be a duplicate. – sharpner Apr 20 '15 at 10:55
  • For me, I don't like third_party_libs which have funcs returning interface{}. To my mind it's untyped, hard to understand and use, bad practice. I'd just avoid to use such a lib. I much prefer SomeThirdPartyMethod() which returns something explicitly - say SomeStruct, or SomeInterface, but not interface{} – Uvelichitel Apr 20 '15 at 11:59
  • it would be the same if the return type would be of an interface lets say... InterfaceA und it optionally may implemented interfaceB. – sharpner Apr 22 '15 at 09:40

2 Answers2

6

In Go, an interface is just a set of methods (spec: Interface types).

The interface does not specify whether receivers are pointers or not. Implementing an interface in Go is implicit: there is no declaration of intent.

You need to read and understand Method sets. Quoting an important part of it:

The method set of any other type T consists of all methods declared with receiver type T. The method set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T (that is, it also contains the method set of T).

A value (which may or may not be a pointer) implements an interface if the method set of its type is a superset of the interface (the methods of the interface type).

In your example:

func (s *SomeStruct) SomeMethodWhichNeedsAPointerReceiver() string{}

SomeMethodWhichNeedsAPointerReceiver() has a pointer receiver, so a value of type SomeStruct will not have this method, but a value of type *SomeStruct will.

So as a consequence a value of type SomeStruct does not implement your SomeInterface interface (because it does not have a SomeMethodWhichNeedsAPointerReceiver() method), but *SomeStruct does implement your SomeInterface because its method set contains the SomeMethodWhichNeedsAPointerReceiver() method.

Since you create and use a simple SomeStruct value (and not a pointer to it), the type assertion will fail.

Should you have used a *SomeStruct, the type assertion would have succeeded.

Modify your SomeThirdPartyMethod() function to create and return a *SomeStruct (a pointer) and it will work as you expect it:

func SomeThirdPartyMethod() interface{} {
    return &SomeStruct{}
}

Or as an alternative:

func SomeThirdPartyMethod() interface{} {
    return new(SomeStruct)
}

Try it on the Go Playground.

If you can't modify SomeThirdPartyMethod()

If you can't modify the SomeThirdPartyMethod() function: first of all, is it intended/required that it returns a value which implements SomeStruct? If so, then its current implementation is wrong, and you can expect it to return a value which does implement SomeStruct without you having to dereference it (in order to gain a value which implements SomeStruct).

In this specific case you can also try type assertion for SomeStruct itself:

if ss, ok := s.(SomeStruct); ok {
    fmt.Println(ss.SomeMethodWhichNeedsAPointerReceiver())
}

Calling ss.SomeMethodWhichNeedsAPointerReceiver() will automatically dereference ss to take its address (which will be the pointer receiver value for calling the SomeMethodWhichNeedsAPointerReceiver() method).

icza
  • 389,944
  • 63
  • 907
  • 827
  • well, the third party method specifies the interface as optional. So therefore it must not be implemented, and the return value is interface{}. It's not elegant, but that's how it is. – sharpner Apr 20 '15 at 10:57
4

The best solution would indeed be to return a pointer from SomeThirdPartyMethod. But if this is not possible, you can construct a pointer to that value using reflection:

v := reflect.Indirect(reflect.New(reflect.TypeOf(s)))
v.Set(reflect.ValueOf(s))
sp := v.Addr().Interface()
casted, ok = (sp).(SomeInterface)
if ok {
    fmt.Println("Good as well: " + casted.SomeMethodWhichNeedsAPointerReceiver())
    return
}

Working example: http://play.golang.org/p/JYJT8mRxWN

Ainar-G
  • 34,563
  • 13
  • 93
  • 119
  • Yeah that is the only way we found as well. I upvoted you're answer but I will let this question open for now to find other suggestions. But thx for your response for now :D – sharpner Apr 20 '15 at 11:06