Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

go

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)?

like image 994
uiron Avatar asked Jul 14 '15 12:07

uiron


3 Answers

One workaround is to define an inline function that calls personBuilder.

printRetrievedItem(func() interface{} {return personBuilder()});

Playground

like image 149
Grzegorz Żur Avatar answered Nov 17 '22 03:11

Grzegorz Żur


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.

like image 4
Simon Avatar answered Nov 17 '22 03:11

Simon


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())
}
like image 3
OneOfOne Avatar answered Nov 17 '22 03:11

OneOfOne