Cannot use implementation of interface as argument to func that wants interface - go

I'm getting the following error:
./main.go:31: cannot use telegramService (type messaging.TelegramService) as type mypackage.MessagingService in argument to mypackage.RegisterMessagingService:
messaging.TelegramService does not implement mypackage.MessagingService (wrong type for HandleIncomingMessage method)
have HandleIncomingMessage(telegram.Message) error
want HandleIncomingMessage(mypackage.IncomingMessage) error
I have an interface that describes a messaging service like Telegram or WhatsApp, and an interface that describes an incoming message from one of those services:
// IncomingMessage is a message that comes in on a messaging service
type IncomingMessage interface {
Send() error
}
// MessagingService is a service on which messages can be send (like Telegram or FB Messenger)
type MessagingService interface {
Start()
HandleIncomingMessage(IncomingMessage) error
GetHTTPHandler() http.HandlerFunc
GetCommands() []MessagingCommand
}
The first implementation of MessagingService is for Telegram. The issue is the HandleIncomingMessage function, which currently doesn't really do anything and just looks like this:
// HandleIncomingMessage will take an incoming message and repond to it
func (s TelegramService) HandleIncomingMessage(msg *telegram.Message) error {
return nil
}
The issue is that this function accepts a telegram.Message, which the compiler says doesn't comply with the interface. The thing is, that telegram.Message is an implementation of IncomingMessage:
// Message is a Telegram message
type Message struct {
// Added the line below at some point, but it didn't work without it either
mypackage.IncomingMessage
MessageID uint64 `json:"message_id"`
FirstName string `json:"first_name"`
Username string `json:"username"`
Date uint64 `json:"date"`
Text string `json:"text"`
Chat Chat `json:"chat"`
From User `json:"from"`
}
// Send will take m and send it
func (m Message) Send() error {
// Do stuff
return nil
}
Initially IncomingMessage was an empty interface, which is where I first noticed the issue. I tried adding the function Send() which I was going to add anyway, as I thought maybe just giving it any struct wouldnt't work. However, I'm still getting this error.
I don't see any reason why telegram.Message doesn't implement the interface, it's pretty straight forward.
Can anyone explain why this doesn't work?
PS: My package isn't actually called mypackage, changed for clarity

HandleIncomingMessage must take an IncomingMessage argument since that's the way the interface is defined. You can't define an implementation of HandleIncomingMessage that takes some other type as the argument, even if that type implements IncomingMessage. You can define your function to take IncomingMessage and convert that to *telegram.Message using a type assertion:
func (s TelegramService) HandleIncomingMessage(im IncomingMessage) error {
msg := im.(*telegram.Message)
return nil
}
I'm assuming you actually want to be using a pointer to telegram.Message. If so, you need to change the definition of the Send method to take a pointer receiver.

Related

How to design an interface returning another interface type which could only be fulfilled emergent

Short premise:
I try to design a package which holds several types fullfilling the same set of interfaces and another package working with these interfaces. Now I'm puzzled how to fulfill an interface returning another interface type.
longer:
To be more specific: I've got two protocol / message types (broadcast / direct) which I want to be able to package to be transported by http/json and amqp/rabbit. My "transport"-packages (json and amqp) require my message-packages (dm/broadcast) to fulfill / present some information: e.g. GetAddress or NewResponse. For NewResponse my message format generates, out of it's metainformation,a ResponseMessage. My transporter knows how to translate GetAddress to a useful routing information. There is my interface problem: Now I'm puzzled how to fulfill an interface returning another interface type.
type Addresser interface {
GetRecipientAddress() string
}
type Responder interface {
NewResponse() (Responder, error)
}
type DM struct {
}
func (dm *DM) GetRecipientAddress() string {
return ""
}
func (dm *DM) NewResponse() (*DM, error) {
return dm, nil
}
As long as my (dm *DM) NewResponse returns Responder the Responder interface is fulfilled. When I return *DM, which fulfills Responder, *DM doesn't fulfill Responder.
I kind of see that this seems to be an chicken or the egg problem, as long as I return *DM, *DM doesn't fulfill Responder, but "de facto" it fulfills it.
That leeds my to my assumption: I try to implement a bad design for the use of interfaces. Can somebody point me in the direction golang-gurus would solve this problem?
I thought maybe this could be a solution, but it seems to be just as flawed
type AddressResponder interface {
Addresser
Responder
}
func (dm *DM) NewResponse() (AddressResponder, error) {
return dm, nil
}
See Type identity:
Two interface types are identical if they have the same set of methods with the same names and identical function types. Non-exported method names from different packages are always different. The order of the methods is irrelevant.
Two function types are identical if they have the same number of parameters and result values, corresponding parameter and result types are identical, and either both functions are variadic or neither is. Parameter and result names are not required to match.
Try this:
// NewResponse returns (Responder, error)
func (dm *DM) NewResponse() (Responder, error) {
return dm, nil
}
type DM can only fill Responder if and only if it has the methods described in Responder. I understand what you are saying, but this is how Golang works!
In other words, DM must have the method of signature NewResponse() (Responder, error) or else it does NOT implement Responder.
Thus perhaps you must change the definition to:
type DM struct {
}
func (dm *DM) NewResponse() (Responder, error) {
return dm, nil
}

Implementing interface type to function type in Golang

I have created few types including interface as:
// GetProfileHandlerFunc turns a function with the right signature into a get profile handler
type GetProfileHandlerFunc func(GetProfileParams, interface{}) middleware.Responder
// Handle executing the request and returning a response
func (fn GetProfileHandlerFunc) Handle(params GetProfileParams, principal interface{}) middleware.Responder {
return fn(params, principal)
}
// GetProfileHandler interface for that can handle valid get profile params
type GetProfileHandler interface {
Handle(GetProfileParams, interface{}) middleware.Responder
}
Now in my api implementation package. I am using a logic to handle the request parameters. I am trying to assign GetProfileHandlerFunc to another type since it implements GetProfileHandler interface as you can see above.
api.ProfileGetProfileHandler = profile.GetProfileHandlerFunc(func(params profile.GetProfileParams, principal *models.User) middleware.Responder {
// contains logic to handle the request
}
Now I think I can do above logic. But I am getting type mismatch error.
cannot convert func literal (type func(profile.GetProfileParams,
*"userproj/models".User)
middleware.Responder) to type profile.GetProfileHandlerFuncgo
the point is:
if you have a function like
func A(param interface{}) {}
you can pass anything to param when you make a call to function A. like
A(10)
A(true)
A(nil)
Because interface{} means everything. So your handle func definition:
type GetProfileHandlerFunc func(GetProfileParams, interface{}) middleware.Responder
means a function GetProfileHandlerFunc which takes two params, the first is of type GetProfileParams, the second is of type interface{}. That means second param canbe anything.
But
func(params profile.GetProfileParams, principal *models.User) middleware.Responder
means a function which takes two params, the first is of type GetProfileParams, the second is of type *models.User. So, do you think they are the same? No.
I need a function can take anything as second param,not a function who can only take User as second function.
When you call your handle
GetProfileHandlerFunc(params, 10) // this is ok
So is that ok for
func(params profile.GetProfileParams, principal *models.User) middleware.Responder
No.
The right way to do this is :
api.ProfileGetProfileHandler = profile.GetProfileHandlerFunc(func(params profile.GetProfileParams, principal interface) middleware.Responder {
user:=principal.(*model.User) // watch this.
}

Call function of specific type in Go

I'm a complete Go newbie, so sorry for the question in advance.
I'm trying to work with a so-defined interface to connect to a message broker:
// Broker is an interface used for asynchronous messaging.
type Broker interface {
Options() Options
Address() string
Connect() error
Disconnect() error
Init(...Option) error
Publish(string, *Message, ...PublishOption) error
Subscribe(string, Handler, ...SubscribeOption) (Subscriber, error)
String() string
}
// Handler is used to process messages via a subscription of a topic.
// The handler is passed a publication interface which contains the
// message and optional Ack method to acknowledge receipt of the message.
type Handler func(Publication) error
// Publication is given to a subscription handler for processing
type Publication interface {
Topic() string
Message() *Message
Ack() error
}
I'm trying to use the Subscribe-function to subscribe to a channel and thats the point where I'm struggeling right now.
My current approach is the following one:
natsBroker.Subscribe(
"QueueName",
func(p broker.Publication) {
fmt.Printf(p.Message)
},
)
The error output is cannot use func literal (type func(broker.Publication)) as type broker.Handler in argument to natsBroker.Subscribe.
But how do I ensure that the function type actually is a broker.Handler?
Thx for your time in advance!
Update
In case anybody is interested, the error return type was missing which caused the error, so it should look similar to that:
natsBroker.Subscribe(
"QueueName",
broker.Handler(func(p broker.Publication) error {
fmt.Printf(p.Topic())
return nil
}),
)
As the error indicates, the parameter and what you're passing don't match:
type Handler func(Publication) error
func(p broker.Publication)
You have no return value. If you add a return value (even if you always return nil), it will work fine.
If your signature of your anonymous function matched that of the handler type declaration (Adrian correctly points out you're missing the error return), you should be able to just do a type conversion:
package main
import "fmt"
type Handler func(int) error
var a Handler
func main() {
a = Handler(func(i int) error {
return nil
})
fmt.Println(isHandler(a))
}
func isHandler(h Handler) bool {
return true
}
Since the the compiler knows at compiler-time that the types match, there's no need to do additional checking, like you might in the case of, say, a type assertion.

How pass different structures to function?

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.

name not registered for interface

I am trying to send a concrete implementation over RPC. The RPC methods expects a interface.
The relevant code snippets are:
In package node:
type Commander interface {
Action() string
}
type Approach struct {
Position int
}
func (p Approach) Action() string {
return "Approach"
}
func (t *RPCMethod) RPCAction(command Commander, reply *int) error {
// RPC Method
}
In package main:
import "node"
gob.Register(node.Approach{})
var p = node.Approach{position}
var q node.Commander = p
var reply int
err = client.Call("RPCMethod.RPCAction",&q, &reply)
I have registered the node.Approach with the gob. But on running the main program I am receiving
gob: name not registered for interface: "node.Approach"
Any ideas on what I am doing wrong? Or how to register name?
Yes, you have registered node.Approach with the gob. But then you pass q, which is not node.Approach. Send p instead, because that has the type you have registered.
As I answered on the mailing list, you will need to register the type on the decoding side; in this case, that is the RPC server side. The gob encoder is using reflection to examine the structures and interfaces, but the decoder side must know up front what types might be stored inside an interface because the only thing transmitted over the wire is the name, and it needs to have already seen the type first.

Resources