I'm new to Go and I would like to understand why the code snippet below does not compile. What is the Go way of accepting a function as a function argument that may have any return type?
package main
func main() {
test(a) // Error: cannot use a (type func() string) as type func() interface {} in argument to test
test(b) // Error: cannot use b (type func() int) as type func() interface {} in argument to test
}
func a() string {
return "hello"
}
func b() int {
return 1
}
func test(x func() interface{}) {
// some code...
v := x()
// some more code....
}
Play: https://play.golang.org/p/CqbuEZGy12
My solution based on Volker's answer:
package main
import (
"fmt"
)
func main() {
// Wrap function a and b with an anonymous function
// that has an empty interface return type. With this
// anonymous function, the call signature of test
// can be satisfied without needing to modify the return
// type of function a and b.
test(func() interface{} {
return a()
})
test(func() interface{} {
return b()
})
}
func a() string {
return "hello"
}
func b() int {
return 1
}
func test(x func() interface{}) {
v := x()
fmt.Println(v)
}
You tripped over a very common misconception for Go newcomers: The empty interface interface{}
does not mean "any type". Really, it does not. Go is statically typed. The empty interface interface {}
is an actual (strongly typed type) like e.g. string
or struct{Foo int}
or interface{Explode() bool}
.
That means if something has the type interface{}
it has that type and not "any type".
Your function
func test(x func() interface{})
takes one parameter. This parameter is a (parameterless function) which returns a specific type, the type interface{}
. You can pass any function to test
which matches this signature: "No parameters and return interface{}
". None of your functions a
and b
match this signature.
As said above: interface {}
is not a magical abbreviation for "whatever",it is a distinct static type.
You have to change e.g. a to:
func a() interface{} {
return "hello"
}
Now this might look strange as you return a string
which is not of type interface{}
. This works because any type is assignable to variables of type interface{}
(as every type has at least no methods :-).
As the Go specification states:
A function type denotes the set of all functions with the same parameter and result types
In your case, your result types differ (string
vs interface{}
)
To be able to receive a function with any kind of result type, test
would have to be defined as:
func text(x interface{}) { ... }
and then you will have to use reflect package to call the function stored in x
.
Edit
Such a test
function would look like this:
func test(x interface{}) {
v := reflect.ValueOf(x)
if v.Kind() != reflect.Func {
panic("Test requires a function")
}
t := v.Type()
if t.NumIn() != 0 && t.NumOut() != 1 {
panic("Function type must have no input parameters and a single return value")
}
values := v.Call(nil)
val := values[0].Interface()
// some more code..
}
Playground: https://play.golang.org/p/trC2lOSLNE