Search code examples
gotypesreflection

Can a Go interface receive any function, regardless of its signature?


EDIT: A couple of the answers have suggested changing the signature of the RegisterHandler function, however as part of the original question I asked if it were possible to further restict the types, not for design suggestions. Changing the signature would change the behavior of the function so it's an unsuitable solution.

I have a function that looks a bit like

func (c *Context) RegisterHandler(f interface{}) error {
    // Do lots of checking to make sure a function was passed in
    // and it's arguments all implement a specific interface
}

func consumer(a ConcreteTypeA, b ConcreteTypeB) {}

func main() {
    ...
    c.RegisterHandler(consumer)
}

which roughly boils down to

var a ConcreteTypeA
var b ConcreteTypeB
a.InitFromContext(c)
b.InitFromContext(c)
consumer(a, b)

Is there a way I can make that interface{} more specific without changing its behavior? If not I'm fairly happy with my solution. I just hope I can cut down on runtime checks and possible bugs being uncaught at compile time.

My current implementation looks pretty similar to above. I have tried to do something like

func (c *Context) RegisterHandler(f func (...InitFromContexter)) error {
    // Do lots of checking to make sure a function was passed in
    // and it's arguments all implement a specific interface
}

But that causes all the existing code to fail because it will only receive variadic functions


Solution

  • You have already found the way with your RegisterHandler(f interface{}), there really isn't any other.

    The fact is that if you want to support multiple signatures, you'll also need to have code handling all the various signature patterns you're supporting, so a call can eventually actually be made.

    As you already wrote, that'd be a spaghetti ball of runtime checks and in fact also reflection-based checking and calling. It might be cleaner and more intelligible (especially later on) to have something like RegisterHandler(handler interface{ Do() } where your various different patterns show up as implementation-specific struct fields, if that's feasible in your project.