How to use a type parameter in an interface method? - go

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.

Related

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

Implementing interface from different package golang

I'm having some issues trying to implement an interface, defined in a different package in golang. I have made a minimal recreation of the problem below
Interface:
package interfaces
type Interface interface {
do(param int) int
}
Implementation:
package implementations
type Implementation struct{}
func (implementation *Implementation) do(param int) int {
return param
}
Main.go:
package main
import (
"test/implementing-interface-in-different-package/implementations"
"test/implementing-interface-in-different-package/interfaces"
)
func main() {
var interfaceImpl interfaces.Interface
interfaceImpl = &implementations.Implementation{}
}
Error message:
test/implementing-interface-in-different-package
./main.go:10:16: cannot use implementations.Implementation literal (type
implementations.Implementation) as type interfaces.Interface in assignment:
implementations.Implementation does not implement interfaces.Interface (missing interfaces.do method)
have implementations.do(int) int
want interfaces.do(int) int
Is it possible to implement an interface from a different package?
Thanks!
The problem is that your do function is not exported from the implementations package because it starts with a lower-case letter. Thus from the point of view of the main package, the variable interfaceImpl does not implement the interface because it cannot see the do function.
Rename your interface function to upper-case Do to resolve the problem.

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
}

How to pass interfaces indirectly in golang

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.

Resources