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.
Example third party API usage:
fund doSomething(api *api.Client) {
...
result, err := api.Logical().Write(val1, val2)
...
}
I can handle the initial call to Logical() with my own interface:
type API interface {
Logical() *api.Logical
}
...
doSomething(&MockAPI{}) // Assuming MockAPI implements API
However, this now brings the problem into sight: the Logical() method has to have the above function signature, otherwise, I couldn't substitute the real api object in for my interface. Since the *api.Logical type is nested within the third party API library, I cannot simply mock it out with another interface:
type Writer interface {
Write(string, string) Result, error
}
type API interface {
Logical() *Writer
}
...
doSomething(&api.Client{}) // Doesn't implement Logical() *Writer
How would I mock this API call out so I can return custom data and not hit a live service? If it helps, this is based on an actual API.
I think the best answer here is to just avoid the chained function call altogether.
type Writer interface {
Write(string, string) Result, error
}
func doSomething(writer Writer) {
...
result, err := writer.Write(val1, val2)
...
}
...
doSomething(api.Client.Logical())
Now I can implement my own Writer and use it for mocking.
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
I'm using the Docker API and pass the Client to a function like this:
func DoStuffToDocker(ctx context.Context, c *client.Client, ID string) {
c.ContainerInspect(ctx, ID)
// do stuff
}
I create an interface that defines the functions I use so that I can pass a mock in my tests:
type dockerClient interface {
ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error)
}
... and then change the signature of my function to accept the interface instead of the client.
My problem is that the compiler complains that the client doesn't implement the interface. From what I can tell the reason is that the implementation of ContainerInspect uses the golang.org/x/net/context context which the API has vendored (so the import is actually github.com/docker/docker/vendor/golang.org/x/net/context).
How can I create the interface so that the client implements it?
I'm a beginner in Golang and I'm working on a small library which need to get a DB connection at some point in the code for différent sub package / method call. I'm just wondering how I can manage this ?
Example, If I manage to have a webserver, it works with handler, so how can I get this connection inside this function ?
It could be used with another process, simple method call or MVC model ?
I don't want to use global because for me it's a bad practice except if it's very exceptional way (or tricky somehow).
I read a lot of write in different website, but still, I'm asking and learning from different opinion and experiences.
Thanks for your time !
Create a struct that represent the resource, let's call Cart. Add get and post methods to this struct. These methods should be http handlers. In main create an instance of the struct with db interface. And in the route call Cart.get. Now in get method you have access to the db interface.
Not a working example, just to get the idea of injecting for testing.
type storage interface {
PrepareContext(context.Context, string) (*sql.Stmt, error)
}
func main() {
db, _ := sql.Open("mysql", `queryString`)
http.HandleFunc("/", Cart{db}.get)
http.ListenAndServe(":8080", nil)
}
type Cart struct {
storage
}
func (crt Cart) get(w http.ResponseWriter, r *http.Request) {
q, _ := crt.PrepareContext(context.Background(), `select *`)
fmt.Println(q.Exec())
}
/////////Test
type testDB struct{}
func (c testDB) PrepareContext(context.Context, string) (*sql.Stmt, error) {
return nil, nil
}
func TestGet(t *testing.T) {
db := testDB{}
_ = Cart{db}
//http test here
}
I would suggest Dargo which is an injection engine in the style of Java CDI and/or JSR-330. It uses struct annotations and Injection is performed using reflection or using creator functions. It supports different lifecycles for services including Singleton (created exactly once, lazily), PerLookup (created every time injected or looked up), Immediate (created exactly once, eagerly) and DargoContext (tied to the lifecycle of a context.Context)
You can also try Hiboot which is a web/cli application framework that support dependency injection out of box.
Docs
// HelloService is a simple service interface, with interface, we can mock a fake service in unit test
type HelloService interface {
SayHello(name string) string
}
type helloServiceImpl struct {
}
func init() {
// Register Rest Controller through constructor newHelloController
// Register Service through constructor newHelloService
app.Register(newHelloController, newHelloService)
}
// please note that the return type name of the constructor HelloService,
// hiboot will instantiate a instance named helloService for dependency injection
func newHelloService() HelloService {
return &helloServiceImpl{}
}
// SayHello is a service method implementation
func (s *helloServiceImpl) SayHello(name string) string {
return "Hello" + name
}
// PATH: /login
type helloController struct {
web.Controller
helloService HelloService
}
// newHelloController inject helloService through the argument helloService HelloService on constructor
func newHelloController(helloService HelloService) *helloController {
return &helloController{
helloService: helloService,
}
}
// Get /
// The first word of method name is the http method GET
func (c *helloController) Get(name string) string {
return c.helloService.SayHello(name)
}
I would suggest giving a try to https://github.com/facebookgo/inject. It allows to define an object graph and specify dependencies with struct annotations. Injection is performed using reflection.
For an IoC container, you can try this package:
https://github.com/golobby/container
Example of singleton binding:
container.Singleton(func() Database {
return &MySQL{}
})
Example of resolving:
var db Database
container.Make(&db)
As you can see it's so easy to work with.