Reputation: 41674
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.
Upvotes: 5
Views: 4393
Reputation: 6278
You can create a Content
& ContentList
interfaces
type Content interface {
GetTitle() string
}
type ContentList interface {
Contents() []Content
}
func printResponse(response ContentList) {
for content := range response.Contents() {
fmt.Println(content.GetTitle())
}
}
Then ArticleResponse
& CommentResponse
should implement the ContentList
interface and Aticle
& Comment
should implement the Content
interface.
Upvotes: 0
Reputation: 10158
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.
Upvotes: 7