meesterguyperson
meesterguyperson

Reputation: 1884

How do I do a type assertion on an interface{} variable to test if it is a function?

I am attempting to write a function that accepts either some type of content of arbitrary type or a function that can generate and return an arbitrary type. To do this, I have to be able to generally test if an argument is a function without testing for whether it is a function of return type X. How do I do this? Might look something like the following:

func Blah(arbitrary interface{}) {
    var value interface{}

    if function, ok := arbitrary.(func interface{}); ok {
        value = function()
    } else {
        value = arbitrary
    }
    ...
}

This fails as is. Maybe a type assertion isn't the thing to use here. Or maybe I just don't know the syntax. Would appreciate any suggestions. Only thing I know to do at present is to divide this up into two functions, one that accepts data to be stored as is, and another that expects to get a function, but that seems overkill when in both cases the goal is simply to get a value and pass it on to the rest of the function.

Any ideas on this?

Upvotes: 1

Views: 2729

Answers (1)

Simon Fox
Simon Fox

Reputation: 6425

Type assertions work on specific types. If you know that the type of the function always has type func() interface{}, then you can use:

if f, ok := arbitrary.(func() interface{}) {
    value = f()
} else {
    value = arbitrary
}

Note that the type func() interface{} is a function with return type interface{}, not a function with an arbitrary return type.

It's easy to wrap an arbitrary function to a func() interface{}:

func hello() string { return "hello" }

wrapped := func() interface{} { return hello() }

If you cannot arrange for the function to have return type interface{}, then you need to use reflection to test and call the function:

func isFunction(v interface{}) (func() interface{}, bool) {
  rv := reflect.ValueOf(v)
  if rv.Kind() == reflect.Func {
    return func() interface{} {
        result := rv.Call(nil)
        if len(result) > 0 {
            return result[0].Interface()
        }
        return nil
    }, true
  }

  return nil, false
}

---

if f, ok := isFunction(arbitrary); ok {
    value = f
} else {
    value = arbitrary
}

playground

Upvotes: 5

Related Questions