Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass function reference with unknown parameter type in Go

Tags:

go

I am working with a third-party library with two functions that each return different types. E.g. ArticleResponse and CommentResponse.

I would like to pass the results of calling either of those functions into my own function. As a second argument to that function I would like to pass a function reference that describes how to print that response to stdout.

response := GetArticles()
processResponse(response, printResponse)

func printResponse(response <what_type?>) {
    for i := range response.Articles {
        fmt.Println(response.Articles[i].Title)
    }
}

It's not clear to me how to coerce or create generic types so that the printResponse function knows what to expect to be passed in its parameters.

If I haven't provided a good enough description of what I'm trying to do here, please let me know and I will edit/update the question.

like image 749
Andy Hume Avatar asked Oct 20 '17 15:10

Andy Hume


People also ask

Can I pass a function as a parameter in Go?

Information can be passed to functions as a parameter. Parameters act as variables inside the function.

Which Golang datatype can be used to store values of unknown type?

Empty interfaces are used by code that handles values of unknown type. For example, fmt.

What is Variadic function in Golang?

A variadic function is a function which accepts a variable number of arguments. It can be used when the number of input params are unknown. The code: getNames.go. As we can see the variadic func (getNames() accepts variable number of input values — zero or more.

What is type T in Golang?

While v is just an ordinary parameter, of some unspecified type, T is different. T is a new kind of parameter in Go: a type parameter. We say that PrintAnything[T] is a parameterized function, that is, a generic function on some type T.


1 Answers

Your only real option in this case is for processResponse to accept an interface{} and a function that accepts the same, and then for printResponse to accept the same empty interface and type-assert it (or use a type switch). For example:

func main() {
    response := GetArticles()
    processResponse(response, printResponse)
}

func processResponse(response interface{}, printResponse func(interface{})) 
{
    // Process
    printResponse(response)
}

func printResponse(response interface{}) {
    switch r = reponse.(type) {
    case ArticleResponse:
        for i := range r.Articles {
            fmt.Println(r.Articles[i].Title)
        }
    case CommentResponse:
        for i := range r.Comments {
            fmt.Println(r.Comments[i].Topic, r.Comments[i].User)   
        }
    }
}

However, a more common style would be for your response itself to have a Print method (or similar), and for your process function to accept an interface representing that common method. For example:

type ArticleReponse struct {
    // ...
}

func (a ArticleReponse) Print() {
    for i := range a.Articles {
        fmt.Println(a.Articles[i].Title)
    }
}

type CommentResponse struct {
    // ...
}

func (c CommentResponse) Print() {
    for i := range c.Comments {
        fmt.Println(c.Comments[i].Topic, c.Comment[i].User)
    }
}

type Response interface {
    Print()
}

func main() {
    response := GetArticles()
    processResponse(response)
}

func processResponse(response Response) 
{
    // Process
    response.Print()
}

This style allows the response types themselves to define their printing behavior, while the processResponse function only knows that it got some type that's capable of printing itself. This also allows you to add other methods to the Response interface that processResponse (or anything else) might need in order to interact with those types, without actually having to know which type it's been given. This makes your code substantially less brittle, as it's no longer dependent on the actual implementation details of each response type. It also allows you to unit test processReponse in isolation by mocking out the Response interface.

like image 125
Kaedys Avatar answered Sep 25 '22 02:09

Kaedys