Yaron Schwimmer
Yaron Schwimmer

Reputation: 5357

Type switch to validate function by interface

I want to test a given function in runtime, to verify that it matches a certain signature. It's sufficient that this signature complies with some interface, not a specific implementation.

I can't seem to find the right pattern to do such validation. In runtime, the function's type is using the implementation.

How can I compare the function's signature to the interface?

package main

import "fmt"

type myinteface interface{
    DoSomething(int) string
}

type myfunc func(myinteface)

type impl struct {}

func (im *impl) DoSomething(int) string{
    return "fsdfsd"
}

func do(i interface{}) {
    switch v := i.(type) {
    case func(myinteface):  
        fmt.Print("doesn't stop here")
    case func(impl):    
        fmt.Print("this does work")
    default:
        fmt.Printf("I don't know about type %T!\n", v)
    }
}

func test(im impl) {}

func main() {
    do(test)
}

see it live in playground

Upvotes: 3

Views: 2728

Answers (2)

Hein Oldewage
Hein Oldewage

Reputation: 290

From your question it is a little hard to tell what you want to assert about the function. I wrote up something that I think does what you need.

Note that the function test in your question did not take a parameter that implements myinteface as it was not a pointer.

As mentoined, you probably need to find a different solution to your problem you are trying to solve using this.

package main

import "fmt"
import "reflect"

type myinteface interface {
    DoSomething(int) string
}

type myfunc func(myinteface)

type impl struct{}

func (im *impl) DoSomething(int) string {
    return "fsdfsd"
}

//Does the function passed as `i` take a type that implements the `myinteface` type as first parameter
func do(i interface{}) {
    functionType := reflect.TypeOf(i)
    if functionType.Kind() != reflect.Func {
        fmt.Printf("(%s) is not a function", functionType)
    }
    ourInterfaceType := reflect.TypeOf((*myinteface)(nil)).Elem()
    if functionType.In(0).Implements(ourInterfaceType) {
        fmt.Printf("(%s)'s first paramter implements the `myinteface` interface\n", functionType)
    } else {
        fmt.Printf("(%s)'s first paramter does not implement the `myinteface` interface\n", functionType)
    }
}

func test(im *impl) {}
func test2(im impl) {}

func main() {
    do(test)
    do(test2)
}

Upvotes: 0

Eugene Lisitsky
Eugene Lisitsky

Reputation: 12875

You may check arguments type manually using reflect package. Here's an example for myinteface. I check whether first parameter Implements desired interface. For brevity I suppose function has only one obligatory argument. For production it's better to check number of arguments (commented out string with NumIn()) and all their types in a cycle.

func do(i interface{}) {
    val := reflect.ValueOf(i)
    typ := val.Type()
    // numIn := typ.NumIn()
    arg0 := typ.In(0)
    modelType := reflect.TypeOf((*myinteface)(nil)).Elem()
    if arg0.Implements(modelType) {
        fmt.Println("OK")
    } else {
        fmt.Println("not OK")
    }
}

Also, please pay attention to a receiver type: value or pointer. For example, here only test2 function argument implements myinteface, but test doesn't because of the pointer receiver on the struct impl.

func test(im impl) {}

func test2(im *impl) {}

func main() {
    do(test)
    do(test2)
}

Demo: https://play.golang.org/p/ZDZKZIh2lW

If you change struct definition to value receiver then both function's arguments implement it:

type impl struct{}

func (im impl) DoSomething(int) string {
    return "fsdfsd"
}

https://play.golang.org/p/xvasgBs-_a

Upvotes: 2

Related Questions