如何使用interface {}作为灵活的函数参数和返回类型?

I'm a beginner with Go, and I'm now writing a function which can call an API. The function receives a part of the url (/user, /account, etc) and the struct to convert the returned json to (the structs User or Account for example) as arguments.

So I now have this:

func (self *RestClient) request(action string, return_type interface{}) interface{} {
    res, _ := goreq.Request{Uri:self.url + action}.Do()
    var item return_type
    res.Body.FromJsonTo(&item)
    return item
}

And I try to call this function using (with Index being the struct for the return type):

self.request("/api/v1/public/index", Index)

But this doesn't work. I get the following errors:

return_type is not a type
type Index is not an expression

I guess I understand why this is. I think I have to somehow find out the type of return_type and then convert return_type to that type, before using it as the type for item. I have no idea how though. About the second error I have no idea. I don't even understand what is meant by it.

Could anybody help me out? How can I make this work? Or should this be done in a completely different way all together? All tips are welcome!

I'll first say that you should always check for errors from any function that possibly returns one.

The error you are seeing is because you are trying to declare a variable item as type return_type and that is the name of a function argument.

The other error is from Index being a type declaration and not a concrete value of the Index type.

I agree with Volker's comment but to put it in code, you could use something like this:

func (self *RestClient) request(action string, return_type interface{}) {
    res, err := goreq.Request{Uri:self.url + action}.Do()
    if err != nil {
        // Do something with error here.
    }
    res.Body.FromJsonTo(return_type)
}

var index Index
rest_client.request("/some/path", &index)

This allows flexibility but could lead to strange cases if you forget to pass a pointer to the value return_type.

A few hints based on this code:

  • Don't use self - use a meaningful identifier
  • Don't use interface{} to avoid dealing with the type system
  • Don't use reflection
  • Don't ignore errors returned (as from FromJsonTo or goreq.Request)
  • Don't use a library like goreq unless you are sure you need it (you don't) - you are pulling in lots of code you don't need and it is teaching you bad habits like attempting to use empty interface and reflection to solve simple problems.

Have a look at the definition of FromJsonTo - if you look through this library you'll see it isn't saving you much effort and is adding lots of complexity. Here is how you could do it without the library:

func (c *RestClient) Request(action string, resource interface{}) error {
    res, err := http.Get(c.url + action)
    if err != nil {
        return err
    }
    defer res.Body.Close()
    return json.NewDecoder(res.Body).Decode(resource)
}

Or use an interface and move the decoding to the resource (which could embed a default decoder):

type Decoder interface {
    Decode(r io.Reader) error
}

// RequestDecode fetches a request and feeds it to the decoder
func (c *RestClient) RequestDecode(action string, resource Decoder) error {
    res, err := http.Get(c.url + action)
    if err != nil {
        return err
    }
    defer res.Body.Close()
    return resource.Decode(res.Body)
}