how to append different functions but the same interface in slice - go

I tried to append different functions that can be represented with the same interface.
Functions return different objects but same interface.
It failed with the error cannot use Test (value of type func() *Dog) as func() Animal value in argument to append (typecheck)
What should I do? Thanks in advance!
package main
type Dog struct {
Word string
}
type Cat struct {
Word string
}
func (d *Dog) Say() string {
return d.Word
}
func (c *Cat) Say() string {
return c.Word
}
type Animal interface {
Say() string
}
func main() {
funcs := []func() Animal{}
funcs = append(funcs, Test) // error| cannot use Test (value of type func() *Dog) as func() Animal value in argument to append (typecheck)
}
func Test() *Dog {
return &Dog{Word: "dog"}
}
func Test2() *Cat {
return &Cat{Word: "cat"}
}

Change your functions to have Animal as their return type. func() *Dog is not convertible to func() Animal, they are two separate data types.
Similar to how you can pass, say, int as interface{}, but not []int as []interface{}

The slice element and functions have different return types. Use anonymous functions to convert the function return values to the slice element return type.
funcs = append(funcs,
func() Animal { return Test() },
func() Animal { return Test2() })
for _, f := range funcs {
fmt.Println(f().Say())
}
Run it on the Playground.
Another option is to use the reflect package to call the function and convert the result to an Animal.
func makeAnimal(f interface{}) Animal {
// This function assumes that f is a function
// that returns a value that satisfies the
// Animal interface.
result := reflect.ValueOf(f).Call(nil)
return result[0].Interface().(Animal)
}
Use it like this:
funcs := []interface{}{}
funcs = append(funcs, Test, Test2)
for _, f := range funcs {
a := makeAnimal(f)
fmt.Println(a.Say())
}
Run it on the Playground.

The problem is that func () *Dog cannot converts to func() Animal.
If you don't wanna use reflection, you have to change the "funcs" type to []interface{} then cast each element of the slice into func() *Dog and simply call it, like this:
package main
import "fmt"
type Dog struct {
Word string
}
type Cat struct {
Word string
}
func (d *Dog) Say() string {
return d.Word
}
func (c *Cat) Say() string {
return c.Word
}
type Animal interface {
Say() string
}
func main() {
var funcs []interface{}
funcs = append(funcs, Test)
fmt.Println(funcs[0].(func() *Dog)().Say()) // prints "dog"
}
func Test() *Dog {
return &Dog{Word: "dog"}
}
func Test2() *Cat {
return &Cat{Word: "cat"}
}

Related

How to pass func with any return type as parameter into another function?

func F(f func()interface{})interface{} {
return f()
}
func one() int {
return 1
}
type A struct {}
func two() A {
return A{}
}
func main() {
a := F(one)
b := F(two)
}
The code above will fail with error
cannot use one (type func() int) as type func() interface {} in argument to F
cannot use two (type func() A) as type func() interface {} in argument to F
My question is how to pass a func with any possible output as a parameter?
A value of type int can be assigned to an interface{} variable; a value of type func() int can not be assigned to a value of type func() interface{}. This is true with any version of Go.
Though, what you are attempting to do can be achieved with Go 1.18, where you can easily parametrize the function with T any — where any is an alias of interface{}:
func Callf[T any](f func() T) T {
return f()
}
func one() int {
return 1
}
type A struct {}
func two() A {
return A{}
}
func main() {
a := Callf(one)
b := Callf(two)
fmt.Println(a) // 1
fmt.Println(b) // {}
}
Playground: https://go.dev/play/p/zCB5VUhQpXE

Dynamic function call in Go

I'm trying to dynamically call functions returning different types of struct.
For example, let's take the following code.
struct A {
Name string
Value int
}
struct B {
Name1 string
Name2 string
Value float
}
func doA() (A) {
// some code returning A
}
func doB() (B) {
// some code returning B
}
I would like to pass either the function doA or doB as an argument to a generic function that would execute the function and JSON-encode the result. Like the following:
func Generic(w io.Writer, fn func() (interface {}) {
result := fn()
json.NewEncoder(w).Encode(result)
}
But when I do:
Generic(w, doA)
I get the following error:
cannot use doA (type func() (A)) as type func() (interface {})
Is there a way to achieve this dynamic call?
First, let me remark that func() (interface{}) means the same thing as func() interface{}, so I'll use the shorter form.
Passing a function of type func() interface{}
You can write a generic function that takes a func() interface{} argument as long as the function that you pass to it has type func() interface{}, like this:
type A struct {
Name string
Value int
}
type B struct {
Name1 string
Name2 string
Value float64
}
func doA() interface{} {
return &A{"Cats", 10}
}
func doB() interface{} {
return &B{"Cats", "Dogs", 10.0}
}
func Generic(w io.Writer, fn func() interface{}) {
result := fn()
json.NewEncoder(w).Encode(result)
}
You can try out this code in a live playground:
http://play.golang.org/p/JJeww9zNhE
Passing a function as an argument of type interface{}
If you want to write functions doA and doB that return concretely typed values, you can pass the chosen function as an argument of type interface{}. Then you can use the reflect package to make a func() interface{} at run-time:
func Generic(w io.Writer, f interface{}) {
fnValue := reflect.ValueOf(f) // Make a concrete value.
arguments := []reflect.Value{} // Make an empty argument list.
fnResults := fnValue.Call(arguments) // Assume we have a function. Call it.
result := fnResults[0].Interface() // Get the first result as interface{}.
json.NewEncoder(w).Encode(result) // JSON-encode the result.
}
More concisely:
func Generic(w io.Writer, fn interface{}) {
result := reflect.ValueOf(fn).Call([]reflect.Value{})[0].Interface()
json.NewEncoder(w).Encode(result)
}
Complete program:
package main
import (
"encoding/json"
"io"
"os"
"reflect"
)
type A struct {
Name string
Value int
}
type B struct {
Name1 string
Name2 string
Value float64
}
func doA() *A {
return &A{"Cats", 10}
}
func doB() *B {
return &B{"Cats", "Dogs", 10.0}
}
func Generic(w io.Writer, fn interface{}) {
result := reflect.ValueOf(fn).Call([]reflect.Value{})[0].Interface()
json.NewEncoder(w).Encode(result)
}
func main() {
Generic(os.Stdout, doA)
Generic(os.Stdout, doB)
}
Live playground:
http://play.golang.org/p/9M5Gr2HDRN
Your return signature is different for these functions:
fn func() (interface {}) vs. func doA() (A) and func doB() (B)
You are getting a compiler error because you are passing a function with a different signature into your Generic function. To address this issue you can change your functions to return interface{}.
This is an example of how to do that, I am using anonymous structs and printing the return value out rather than serializing them but this applies just the same to your example:
package main
import "fmt"
func doA() interface{} {
return struct {
Name string
Value int
}{
"something",
5,
}
}
func doB() interface{} {
return struct {
Name1 string
Name2 string
Value float64
}{
"something",
"or other",
5.3,
}
}
func main() {
fmt.Println("Hello, playground", doA(), doB())
}
Experiment with this in the Go Playground: http://play.golang.org/p/orrJw2XMW8

Accept function in argument with empty interface return type

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)
}
Play: https://play.golang.org/p/waOGBZZwN7
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

golang: how is "func() interface {}" and "func() *MyStruct" incompatible types?

Suppose I have code, where a function accepts another one as an argument:
type Person struct {
Name string
}
func personBuilder() * Person {
return &Person{Name: "John"}
}
func printRetrievedItem(callback func() interface {}){
fmt.Print(callback());
}
func doStuff(){
printRetrievedItem(personBuilder);
}
This results in error cannot use personBuilder (type func() *Person) as type func() interface {} in function argument. If I change personBuilder return type to interface{}, it works, but in real project I'm working on I want to have a concrete type for clear design and TDD purposes.
Does Go support such method signature generalization? What are the workarounds, if you could not change the personBuilder part (e.g. you have a lot parameterless functions that return different type of struct, and you want to build a consumer function that accepts any of those builders as argument)?
One workaround is to define an inline function that calls personBuilder.
printRetrievedItem(func() interface{} {return personBuilder()});
Playground
You can create an interface with a method that returns an interface{}:
type Thinger interface {
Thing() interface{}
}
func (p *Person) Thing() interface{} {
return p
}
func printRetrievedItem(t Thinger){
fmt.Print(t.Thing());
}
func doStuff(){
printRetrievedItem(personBuilder);
}
This is just an example, please use a better name!
To answer your question, fun() A is not a func() interface{}, for the same reason that []A is not an []interface{}. It's explained very well in the go wiki.
Either do a wrapper like #GrzegorzŻur suggested or define your own interface and make your xxxxBuilder return it:
type Namer interface {
Name() string
}
type Person struct {
name string
}
func (p *Person) Name() string {
return p.name
}
func personBuilder() Namer {
return &Person{name: "John"}
}
func printRetrievedItem(callback func() Namer) {
fmt.Printf("%T: %v", callback(), callback().Name())
}
You can use pkg reflect for this. (Note however that the solution of #OneOfOne is more idiomatic).
package main
import (
"fmt"
"reflect"
)
type Person struct {
Name string
}
func personBuilder() *Person {
return &Person{Name: "John"}
}
func printRetrievedItem(callback interface{}) {
vals := reflect.ValueOf(callback).Call([]reflect.Value{})
fmt.Println(vals[0].Interface())
}
func main() {
printRetrievedItem(personBuilder) // &{John}
printRetrievedItem(func() string { return "hello" }) // hello
}
Here's an example in the playground.

Golang polymorphic parameters and returns

Say I have functions:
func ToModelList(cats *[]*Cat) *[]*CatModel {
list := *cats
newModelList := []*CatModel{}
for i := range list {
obj := obj[i]
newModelList = append(newModelList, obj.ToModel())
}
return &newModelList
}
func ToModelList(dogs *[]*Dog) *[]*DogModel {
list := *dogs
newModelList := []*DogModel{}
for i := range list {
obj := obj[i]
newModelList = append(newModelList, obj.ToModel())
}
return &newModelList
}
Is there a way to combine those two so I can do something like
func ToModelList(objs *[]*interface{}) *[]*interface{} {
list := *objs
// figure out what type struct type objs/list are
newModelList := []*interface{}
// type cast newModelList to the correct array struct type
for i := range list {
obj := obj[i]
// type cast obj based on objs's type
newModelList = append(newModelList, obj.ToModel())
}
return &newModelList
}
First, slices are already a reference, unless you need to change the slice itself, you do not need to pass it as a pointer.
Second, an interface{} can be regardless an object or a pointer to an object. You do not need to have *interface{}.
I am not sure what you are trying to achieve but you could do something like this:
package main
// Interface for Cat, Dog
type Object interface {
ToModel() Model
}
// Interface for CatModel, DogModel
type Model interface {
Name() string
}
type Cat struct {
name string
}
func (c *Cat) ToModel() Model {
return &CatModel{
cat: c,
}
}
type CatModel struct {
cat *Cat
}
func (c *CatModel) Name() string {
return c.cat.name
}
type Dog struct {
name string
}
func (d *Dog) ToModel() Model {
return &DogModel{
dog: d,
}
}
type DogModel struct {
dog *Dog
}
func (d *DogModel) Name() string {
return d.dog.name
}
func ToModelList(objs []Object) []Model {
newModelList := []Model{}
for _, obj := range objs {
newModelList = append(newModelList, obj.ToModel())
}
return newModelList
}
func main() {
cats := []Object{
&Cat{name: "felix"},
&Cat{name: "leo"},
&Dog{name: "octave"},
}
modelList := ToModelList(cats)
for _, model := range modelList {
println(model.Name())
}
}
You define interfaces for your Cat, Dogs etc and for your Model. Then you implement them as you want and it is pretty straight forward to do ToModelList().
you can make *CatModel and *DogModel both implement type PetModel {} interface, and just return []Pet in function signature.
func (cats []*Cat) []PetModel {
...
return []*CatModel {...}
}
func (dogs []*Dog) []PetModel {
...
return []*DogModel {...}
}
BTW: return a pointer of a slice in golang is useless.
If you strip away redundant assignments, and unnecessary pointers-to-slices, you'll find you have little code left, and duplicating it for each of your model types doesn't look so bad.
func CatsToCatModels(cats []*Cat) []*CatModel {
var result []*CatModel
for _, cat := range cats {
result = append(result, cat.ToModel())
}
return result
}
Unless this code is used in a lot of places I'd also consider just inlining it, since it's trivial code and only 4 lines when inlined.
Yes, you can replace all the types with interface{} and make the code generic, but I don't think it's a good tradeoff here.

Resources