How pass different structures to function? - go

I have several different structures.
Here show two:
type AdsResponse struct {
Body struct {
Docs []struct {
ID int `json:"ID"`
// others
} `json:"docs"`
} `json:"response"`
Header `json:"responseHeader"`
}
type OtherResponse struct {
Body struct {
Docs []struct {
ID int `json:"ID"`
// others
} `json:"docs"`
} `json:"response"`
Header `json:"responseHeader"`
}
but i don't know how i can do for this method accepts and return both.
func Get(url string, response Response) (Response, bool) {
res, err := goreq.Request{
Uri: url,
}.Do()
// several validations
res.Body.FromJsonTo(&response)
return response, true
}
And use like this:
var struct1 AdsResponse
var struct2 OtherResponse
Get("someURL", struct1)
Get("someURL", struct2)
There are any form?

Your code example is somewhat confusing since both structs appear to be identical. I'll assume that they differ somewhere in "others".
First, I generally recommend creating a wrapper around these kinds of JSON deserializations. Working directly on the JSON structure is fragile. Most of your program should not be aware of the fact that the data comes down in JSON. So for instance, you can wrap this in an Ads struct that contains an AdsResponse, or just copies the pieces it cares about out of it. Doing that will also make some of the below slightly easier to implement and less fragile.
The most common solution is probably to create an interface:
type Response interface {
ID() int
}
You make both Ads and Others conform to Response. Then you can return Response. If necessary, you can type-switch later to figure out which one you have and unload other data.
switch response := response.(type) {
case Ads:
...
case Other:
...
}

I don't quite get why you have the reponse as a parameter and as a return. I think you dont need to return it. You should pass a pointer to the reponse and fill it with the data. Also, I'd return an Error instead of a boolean, but that is another topic.
Anyway, the solution is to use interface{} (empty interface).
You are lucky because the function you are using (FromJsonTo) accepts an empty interface as a parameter, so you can safely change your parameter type to interface{} and just pass it to FromJsonTo. Like this:
func Get(url string, response interface{}) bool {
res, err := goreq.Request{
Uri: url,
}.Do()
// several validations
res.Body.FromJsonTo(response)
return true
}
Warning: I did not compile the code.
Then you would call this function with the url and a pointer to one of the reponse structs like this:
var struct1 AdsResponse
var struct2 OtherResponse
Get("someURL", &struct1)
Get("someURL", &struct2)

The way to achieve this is through Go's interfaces.
Two options:
empty interface
Get(url string, response interface{}) (Response, bool)
This option allows any value to be given to this function.
custom interface
Creating a custom interface will allow you to narrow down the types that can be provided as arguments to your function.
In this case you'll have to create an interface that all your Response structs will need to abide by. Any struct really that abides by that interface will be able to be used as an argument of your function.
Something like this:
type MyResponse interface {
SomeFunction()
}
Then your function signature could look like
Get(url string, response MyResponse) (MyResponse, bool)
As long as AdsResponse and OtherResponse abide by the MyResponse interface, they will be allowed to be used as arguments to the function.

Follow the solution working at Go Playground
Go has no polymorphic or any other OO like behaviour, so, when you try to pass a AdsResponse or OtherResponse struct as an Response (or any interface{}), these values becomes an Response (or other param type specified), and is not possible to Go to infer the real type that originate these interface{} and correctly decode your json to these struct types as expected.
This kind of thing should works perfectly in OO languages, like Java, C# etc. There is no hierarchy generalization/specialization on structs/interfaces in Go.
You would need to do a type assertion in your Rest executor, or a switch case, but it seems that you need a generic REST executor, like a generic lib some thing like that. Would not reasonable create a switch case for each struct in your program. Maybe you have dozens or hundreds of structs soon.
I think that a reasonable solution is the rest client pass a lambda function to do the last step for your, that is just create a correct struct destination type and call json decode.
As i say above, the return type of executeRest() in my example will became an interface{}, but the rest client can securely do the type assertion of returned value after executeRest() call.

Related

Define golang struct as type with arbitrary fields?

I'm very new to golang and the use of interfaces more generally. I've stubbed out some code here:
type Alerter interface {
Alert()
}
type AlertConfig struct{}
type Alert struct {
Config *AlertConfig
}
type AlertConfigurator interface {
UpdateConfig(key, value interface{}) (*AlertConfig, error)
}
type EmailAlertConfig AlertConfig {
Recipients []mail.Address,
}
type HTTPAlertConfig AlertConfig {
Method string
TargetURL url.URL
}
type HTTPAlert struct {
Config *HTTPAlertConfig
}
type EmailAlert struct {
Config *EmailAlertConfig
}
func (ea *EmailAlert) Alert() {
// this would actually send an email using data in its Config
return
}
func (ha *HTTPAlert) Alert() {
// this would actually hit an HTTP endpoint using data in its Config
return
}
I'm sure I have all kinds of assumptions & biases that are hobbling my ability to express what I want to accomplish with this:
I want to create different types of "Alert"s. Any alert I create should have an "Alert()"
method that triggers the Alert to actually do something (send an email, or POST to a URL,
for example.
The trouble comes in representing an Alert's "Config". Different Alerts have different fields
in their Configs. However, for each Alert type, specific fields are required to be there.
To accomplish that I wanted to create a base type "AlertConfig" as a struct with arbitrary fields,
then define, say, EmailAlertConfig as type 'AlertConfig', but having these specific fields, and then
type 'HTTPAlertConfig' as type 'AlertConfig' requiring different fields. This way, I can define
the 'Alert' type as having a field 'Config *AlertConfig'.
I can almost emulate what I want if AlertConfig is defined as map[string]interface{}, but
in this case I can't leverage golang's checking to validate that an EmailConfig has the required fields.
It seems pretty clear that I'm thinking about this the wrong way. I could use a hand in getting on the right track & appreciate your advice.
Declare a type with the common fields:
type AlertConfig struct {
ExampleCommonField string
}
Embed that type in actual configurations:
type HTTPAlertConfig struct {
AlertConfig
Method string
TargetURL url.URL
}
Based on the code in the question, the alert and config types can be combined.
func (ha *HTTPAlertConfig) Alert() {
// this will hit an HTTP endpoint using data in the receiver
return
}
One way to deal with this problem is to leave configs as purely implementation specific:
type HTTPAlert struct {
Config *HTTPAlertConfig
}
func (a *HTTPAlert) Alert() {...}
type EmailAlert struct {
Config *EmailAlertConfig
}
func (e *EmailAlert) Alert() {...}
With this implementation, the actual Alert implementation can use the type-specific alert configuration, leaving the problem of initializing the config.
When you create the instance of an alert, you can perform type-specific initialization. For instance:
var alerts = map[string]func(configFile string) Alert {
"htmlAlert": NewHTMLAlert,
"emailAlert" NewEmailAlert,
}
func NewHTMLAlert(configFile string) Alert {
var alert HTMLAlert
// Read file, initialize alert
return &alert
}
...

How to create type for interface in gRPC Protobuf?

We have common Auth service for all microservices.
When we validate request in response, we send JSON that look like this.
{
Auth: bool,
Body: interface{}
}
While writing proto3 syntax, how can we write something like an interface type?
I have tried using Any type, but I am not able to use it as we need to pass message type in type url which I am not able to generate.
Proto message for response
message AuthResponse {
bool Auth = 1;
google.protobuf.Any Body = 2;
}
Can anyone help here?
You can set the body type to bytes.
If you have a pre-defined set of types that your body could be, then I suggest you change your Auth struct to something like this:
type AuthResponse struct {
Auth bool
Body Encodable
}
// Encodable defines types that can be encoded into a byte slice.
type Encodable interface {
Encode() []byte
}
Then, implement Encode() []byte on each of your types.
Alternatively, if your Body field can be anything, then perhaps a similar solution, except your Encode function would look like this:
func Encode(interface{}) []byte
In this scenario, though, you may have to use some reflection magic in order to encode it to some byte slice. You could even use JSON.
I hope this helps!

Go: Access a struct's properties through an interface{}

I am having trouble in accessing the one struct's properties (named Params) in different file.
please consider x.go where i invoke a function(CreateTodo)
type Params struct {
Title string `json:"title"`
IsCompleted int `json:is_completed`
Status string `json:status`
}
var data = &Params{Title:"booking hotel", IsCompleted :0,Status:"not started"}
isCreated := todoModel.CreateTodo(data) // assume todoModel is imported
now CreateTodo is a method on a struct (named Todo) in different file lets say y.go
type Todo struct {
Id int `json:todo_id`
Title string `json:"title"`
IsCompleted int `json:is_completed`
Status string `json:status`
}
func (mytodo Todo)CreateTodo(data interface{}) bool{
// want to access the properties of data here
fmt.Println(data.Title)
return true
}
Now I just want to use properties of data in CreateTodo function in y.go.
But i am not able to do so and getting following error
data.Title undefined (type interface {} is interface with no methods)
I am sure issue is around accepting struct as an empty interface but i am not able to figure out.
Please help here.Thanks
So you have one of two options, depending on your model:
#1
Switch to data *Params instead of data interface{} as suggested in another answer but it looks like you are expecting different types in this function, if so; check option #2 below.
#2
Use Type switches as follows:
func (t Todo) CreateTodo(data interface{}) bool {
switch x := data.(type) {
case Params:
fmt.Println(x.Title)
return true
// Other expected types
default:
// Unexpected type
return false
}
}
P.S. Be careful with your json tags: it should be json:"tagName". Notice the ""! Check go vet.
You could just type the function parameter:
func (mytodo Todo)CreateTodo(data *Params) bool{
// want to access the properties of data here
fmt.Println(data.Title)
return true
}
See: https://play.golang.org/p/9N8ixBaSHdP
If you want to operate on a Params (or *Params), you must do that.
If you want to operate on an opaque type hidden behind an interface{}, you must do that.
In short, you cannot peek behind the curtain without peeking behind the curtain. Either expose the actual type Params, so that you can look at it, or keep all the code that does look at it elsewhere. The "keep the code elsewhere" is where interface really shines, because it allows you to declare that something otherwise-opaque has behaviors and ask for those behaviors to happen:
type Titler interface {
GetTitle() string
}
If Params has a GetTitle function, it becomes a Titler.
You can now define your CreateTodo as a function that takes a Titler, and then you can pass &data to this function.
This structure is overall quite klunky and it seems much more likely that Todo itself should be an embeddable struct instead, but see a more complete example starting from a stripped-down version of your sample code here, in the Go Playground.

Composition combining data and functions with interfaces and structs

I'm wondering if this is something that's done in Go or if I'm thinking about it all wrong: composing type x interface and type x struct so my interface methods have access to specific data too:
The C programmer in my wants to do this:
type PluginHandler interface {
onLoad()
pm *PluginManager
}
func (ph PluginHandler) onLoad() {
pm.DoSomething()
}
There I have an interface defined with a function, but also some data I want to pass to those functions but this is a syntax error.
So is this something that's doable in Go through some other method or am I just thinking about the problem wrong?
You have defined onLoad incorrectly. You cannot define a function directly on interface type.
Once you have an interface, you need another type to implement methods specified in the interface. For example, if another type implements onLoad method, they automatically (implicitly) implement the interface PluginHandler.
The other thing you need to do is change the interface function type to accept the required data:
type PluginHandler interface {
onLoad(*PluginManager)
}
struct SomeType {
// ...
}
func (s SomeType) onLoad(pm *PluginManager) { // SomeType now implements
pm.DoSomething() // PluginHandler interface.
}
This way, you get to inject whichever PluginManager required by PluginHandler.
Also, you can use SomeType as a PluginHandler type whereever required.
func someFuntion(ph PluginHandler) {
// ...
ph.onLoad(pm)
// ...
}
Can be called with an input argument of type SomeType:
s := SomeType{}
someFunction(s)
TL;DR; There is no direct translation to Go.
Long answer:
Go interfaces are only methods.
Go structs are only data (with the possibility of receiver methods).
You can reference, and even embed interfaces within structs:
type Frobnicator interface {
Frobnicate() error
}
type Widget struct {
Frobnicator
WidgetName string
}
But that's not really what you're talking about.
The best answer to your dilema is, I believe: Take a step back. You're focusing on the trees, and you need to look at the forest. Go takes a different approach than C, or classical OO languages like C++ and Java.
Look at the general problem to be solved, and find solutions to that in Go. This can be a painful process (I can say from experience), but it's really the only way to learn the new way of thinking.
Just for the record, you can add extra methods to an existing type, by introducing another (indirection) type as:
type HandlerManager PluginManager
func (x *HandlerManager) onLoad() {
((*PluginManager)(x)).DoSomething()
}
And if you need to go with a more generic solution, a combination of Adapter & Strategy patterns could do:
type PluginHandlerAdapter struct{ _onLoad func() }
func (x *PluginHandlerAdapter) onLoad() {
x._onLoad()
}
Used like (public/private access ignored):
type PluginManager struct {
PluginHandlerAdapter
}
func NewPluginManager() *PluginManager {
res := new(PluginManager)
res._onLoad = res.DoSomething
return res
}

Function as argument, access inner parameter

The package valyala/fasthttp implements the following function type:
type RequestHandler func(ctx *RequestCtx)
It is used in buaazp/fasthttprouter like this:
func (r *Router) Handle(method, path string, handle fasthttp.RequestHandler) {
//...
}
I am trying to wrap these like this (open for suggestions on implementation):
//myapp/router
type Request struct {
fasthttp.RequestCtx
}
type RequestHandler func(*Request)
func Handle(method string, path string, handler RequestHandler) {
//I need to access the fasthttp.RequestCtx stuff in here...
}
How can I achieve this? Or, if this is not the way to go at all, how can I achieve my goal as mentioned below for a router package?
BACKGROUND
Goal: My wish is to wrap tooling packages (sessions, database, routing, etc.) in order to make my app agnostic to the implementation of these packages. I wish to do this primarily for the purpose of being able to extend these with domain-specific functionality, and being able to switch one 3rd party lib for another, if I ever would need to do so. It also makes debugging and logging easier.
Method: I create native types and functions, which map to the functionality of the imported packages.
Problem: I am stuck on how to wrap a foreign (i.e. imported) function type properly.
At all your idea looks very good. Some things you could change:
//myapp/router
// Using a composition is idiomatic go code
// this should work. It can't get better.
type Request struct {
fasthttp.RequestCtx
}
// I would make the RequestHandler as a real Handler. In go it would be
// a interface
type RequestHandler interface{
Request(*Request)
}
// If you have a function, which needs to access parameters from `Request`
// you should take this as an input.
func Handle(method string, path string, req *Request) {
//Access Request via req.Request ...
}
Because if you pass a function or an interface into your function, which needs also Request as input the caller needs to create that before he calls your Handle function. Why not change that function just for the input you really need?

Resources