How to pass interfaces indirectly in golang - go

I have a package with a method:
func Route(router *mux.Router){
subrouter := router.PathPrefix(_API).Subrouter()
subrouter.Path(_FOO).HandlerFunc(foo)
subrouter.Path(_BAR).HandlerFunc(bar)
}
and I would like to remove the external dependency of mux by having a matching interface in my package that simple encompasses all the functionality used above, like so:
type Router interface{
Path(string) Path
PathPrefix(string) Path
}
type Path interface{
HandlerFunc(http.HandlerFunc)
Subrouter() Router
}
func Route(router Router){
subrouter := router.PathPrefix(_API).Subrouter()
subrouter.Path(_FOO).HandlerFunc(foo)
subrouter.Path(_BAR).HandlerFunc(bar)
}
but when I build this I get error:
*mux.Router does not implement api.Router (wrong type for Path method)
have Path(string) *mux.Route
want Path(string) api.Path
but I thought interfaces were implicitly used in golang so I thought that *mux.Route did implement my Path interface.

I thought interfaces were implicitly used in golang
Values are wrapped in interfaces implicitly, but only in specific cases like when passing an implementation to a function with an interface argument:
func X(api.Path) {}
X(&mux.Route{}) // works, implicitly converted to api.Path
or when returning an implementation from a function with an interface return type:
func Y() api.Path {
return &mux.Route{} // works, implicitly converted to api.Path
}
In your question's case the compiler wants a value that has a method with the signature:
Path(string) api.Path
But you're giving it a value with a method with signature:
Path(string) *mux.Route
As you might now, Go types are invariant. Formally:
type A interface { Path(string) *mux.Route }
is not a subtype of
type B interface { Path(string) api.Path }
Therefore this won't work.

Related

How to use a type parameter in an interface method?

I am trying to write a sample program to try and implementing the a data structure using Go generics.
As part of this I want to define a iterator interface. I have the following code:
package collection
type Iterator interface {
ForEachRemaining(action func[T any](T) error) error
// other methods
}
It keeps giving me following error
function type cannot have type parameters
Moving the type parameter to the method also doesn't work:
type Iterator interface {
ForEachRemaining[T any](action func(T) error) error
// other methods
}
Gives error:
methods cannot have type parameters
Is there any way to define generic interface
As the error suggests, methods cannot have type parameters of their own as per the latest design. However, they can use the generics from the interface or struct they belong to.
What you need is to specify the type parameter on the interface type as follows:
type Iterator[T any] interface {
// ...
}
and then use the T as any other type parameter in methods within the interface body. For example:
package main
import "fmt"
type Iterator[T any] interface {
ForEachRemaining(action func(T) error) error
// other methods
}
func main() {
fmt.Println("This program compiles")
}
Try it on Go playground.

Interface with method that has interface{} parameter not working

I'm trying to implement some interfaces in Go.
I have InterfaceA:
type InterfaceA interface{
read(interface{}) string
}
I then have InterfaceB:
type InterfaceB interface{
fetch()
}
I have a function:
func Read(a InterfaceA) {}
I have StructA, which satisfies InterfaceA via it's methods, but instead of having a variable "interface{}", it is passed InterfaceB as such:
type StructA struct {}
func (a *StructA) read(b InterfaceB) string {
resp := b.fetch()
return resp
}
This seems to work for my unit tests, but when I import the package I get an error stating that InterfaceA is not implemented correctly, because it is expecting "read(interface{})" not "read(InterfaceB)". Error:
StructA does not implement InterfaceA (wrong type for read method)
have read(InterfaceB) string
want read(interface {}) string
The reason I'm trying to pass an interface to the read() function is so that I can mock out the "i.fetch()" method, but still test the rest of the read() function.
I also can't pass the read method of StructA an interface parameter (e.g. read(b interface{}), because then b won't have the fetch() method.
Am I doing this wrong? I thought that this was working as my unit tests work. I've only encountered the issue when importing the package.
thanks for the comments. I've managed to get this to work via type type assertions. More details can be found here: golang.org/ref/spec#Type_assertions

Override interface's function callback arguments

I am using a package that has a Router interface, and I have created my own app-specific Router interface that wraps the third party package.
Everything is working well, however one of the methods is throwing a compilation error:
controllers/auth.go:52:17: cannot use func literal (type func(router.Router)) as type func(chi.Router) in argument to c.router.Group
This is the interface of the third party package (chi):
type Router interface {
// ...
// Group adds a new inline-Router along the current routing
// path, with a fresh middleware stack for the inline-Router.
Group(fn func(r Router)) Router
// ...
}
This is my wrapper interface:
type Router interface {
chi.Router
// Custom methods...
}
My usage of the Group function is like so:
type AuthController struct {
router router.Router
// ...
}
func (c *AuthController) SetRoutes() {
c.router.Group(func(r router.Router) {
r.Use(middleware.Anyone)
r.Post("/auth/register", c.Register)
r.Post("/auth/login", c.Authenticate)
r.Post("/auth/token/refresh", c.RefreshToken)
})
c.router.Group(func(r router.Router) {
r.Use(middleware.Authorized)
r.Get("/auth/ping", c.Ping)
r.Post("/auth/logout", c.Logout)
})
}
Why is it screaming at my function callbacks argument type? My wrapper router.Router implements the chi.Router interface, so it should work fine shouldn't it? Am I misunderstanding how Go works here?
I can see how this can be confusing so I will try to break it down. You have this method:
Group(fn func(r Router)) Router
This method takes a function as a parameter. That function must have a specific signature:
func(r Router)
That is, it takes a single argument of type chi.Router and has no return values. However, when you call it:
c.router.Group(func(r router.Router) { /***/ }
You're passing in a function of the wrong signature; your function signature is:
func(r router.Router)
That's not the signature required by the method you're calling, so it won't compile. It doesn't matter if router.Router implements chi.Router; the parameter (a func(router.Router)) passed is not of the expected type (a func(chi.Router)).
This may seem silly at first - after all, any router.Router must implement chi.Router. But, think about it: that method, Group, is expecting to receive a function, to which it can pass any chi.Router. That means it can pass a chi.Router which does not implement router.Router. If it were to accept your function, it would break type safety, and what in Go is meant to be a compile-time error (the error you're getting, in fact) would become a run-time error. Basically, by passing a function with a different (and more strict) argument type, you're expecting a guarantee which that method never offered.
The parameter types aren't the same, so the function type doesn't match what's expected, even though your interface includes the interface from the other package (the type has to match exactly). You need to have your functions take a chi.router and then use a type assertion, i.e., myRouter := r.(Router), to convert to your type.

Why does Go think that this structure does not adhere to this interface?

Let's say I set up two Go interfaces and implement them as follows:
type fooInterface interface {
buildBar() barInterface
}
type barInterface interface {
stuff()
}
type fooStruct struct{}
type barStruct struct{}
func (*fooStruct) buildBar() *barStruct {
return &barStruct{}
}
func (*barStruct) stuff() {}
As soon as I try to assign fooStruct to a fooInterface variable, I get the following error:
cannot use fooStruct literal (type *fooStruct) as type fooInterface in assignment:
*fooStruct does not implement fooInterface (wrong type for buildBar method)
have buildBar() *barStruct
want buildBar() barInterface
Naturally, I can modify buildBar() in fooStruct to return a barInterface and it will work. However, I'm curious as to why Go does not notice that *barStruct adheres to barInterface in this case, especially since this would work in a language like Java (likely because Java interfaces are explicitly implemented).
Go playground example: https://play.golang.org/p/84zymo-YnM
Function type in go is not covariant, and not contravariant.
So to be assignable the signature must match exactly.
References:
https://golang.org/ref/spec#Assignability

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
}

Resources