I want to create interface for dig - go

I'm a novice in golang.
I want to abstract from the implementation of the service container:
type Container interface {
Provide(constructor interface{}, opts ...interface{}) error
Invoke(function interface{}, opts ...interface{}) error
}
but an error occurs:
#13 0.387 cmd/app/main.go:26:40: cannot use container (type *dig.Container) as type application.Container in argument to application.NewApplication:
#13 0.387 *dig.Container does not implement application.Container (wrong type for Invoke method)
#13 0.387 have Invoke(interface {}, ...dig.InvokeOption) error
#13 0.387 want Invoke(interface {}, ...interface {}) error
What am I doing wrong?
How I can declare interface correct?

Cannot use container (type *dig.Container) as type application.Container in argument to application.NewApplication:
*dig.Container does not implement application.Container (wrong type for Invoke method)
have Invoke(interface {}, ...dig.InvokeOption) error
want Invoke(interface {}, ...interface {}) error
This means *dig.Container doesn't satisfy the interface application.Container. It goes on to explain that application.Container requires the Invoke method to take a variadic list of interface{}, but *dig.Container takes a variadic list of dig.InvokeOption.
Even though an empty interface can store any type, that does not mean that any slice type is automatically also a slice of empty interfaces (That's a big motivation for Generics, which should come out soon).
I remember being confused by this myself. Consder this code:
package main
import (
"fmt"
)
type T struct{}
func main() {
var Ts = []T{
T{}, T{}, T{},
}
fmt.Println(Ts...)
}
It will not compile, even thought each individual T satisfies interface{} because a []T is not an []interface{}:
./prog.go:13:16: cannot use Ts (type []T) as type []interface {} in argument to fmt.Println
The easiest solution here would be to make dig.Container's Invoke function match the Invoke signature of application.Container. You can still pass it a list of *dig.InvokeOptions, which individually satisfy interface{}. You'll still have to convert the interface{}s back to *dig.InvokeOptions within dig.Container.Invoke.

Related

How to make a function accept structs that implement generic interfaces?

I have the following generic interface
type Worker[input any, output any] interface {
Process(input) output
}
I'm trying to implement the interface with the following
type IntWorker[i int, o int] struct{}
func (w *IntWorker[i, o]) Process(input i) o {
fmt.Println("running i")
return 1
}
When I try to use this I get the following error
mgr := internal.NewWorkManager()
iwkr := &IntWorker[int, int]{}
mgr.RegisterWorker(iwkr)
cannot use iwkr (variable of type *IntWorker[int, int]) as internal.Worker[any, any] value in argument to : *IntWorker[int, int] does not implement internal.Worker[any, any] (wrong type for method Process)
have Process(input int) int
want Process(any) any
Note the last segment, it says want any but have int. I'm not sure how to fix this since I was under the impression you can pass anything to any
The variable is passed to this function:
func (m *WorkManager) RegisterWorker(f Worker[any, any]) uuid.UUID
Any functions that should take generic variants of the Worker interface must themselves be generic functions.
func TakesAnyWorker[input any, output any](w Worker[input, output]) { ... }
The type Worker[any, any] is not a generic type, and is only compatible with values implementing Process(any) any as that is what your interface definition dictates.
In your example, the function is a method, which cannot take type parameters in Go 1.19. If a single WorkManager will always take Workers of the same type, then you can make the WorkManager a congruent generic type like this:
type WorkManager[input any, output any] struct { ... }
func (m *WorkManager[input, output]) RegisterWorker(f Worker[input, output]) uuid.UUID
If your single WorkManager needs to take Workers of differing types, then I'm afraid you have bigger problems than generics can solve and you'll be forced to use regular interfaces and reflect.

What does it mean for a function to have a method?

I'm seeing the following type definition:
type Getter func(ctx *context.T, key string) (Ticket, error)
And the following method defined on it:
func (g Getter) GetData(ctx *context.T, path ...string) (data []byte, err error) {
…
}
I'm also seeing the following variable definition:
var Client Getter = func(ctx *context.T, key string) (Ticket, error) {
return …
}
where:
type Ticket interface {
…
}
What'll happen with this code exactly?
tl;dr It won't compile because Cannot use 'func() (MyData, error) { return nil, nil }' (type func() (MyData, error)) as the type Getter. In other words, MyGetter does not match the type of Getter. Here's a go playground link.
There are a couple of things going on here:
You're creating a type called Getter. Any value that fulfills this Getter type must be a function with zero input parameters and zero output parameters (keep this in mind for a second).
Go has first-class functions which means they can be assigned to variables, passed to parameters of other functions, or in this case methods can be added to a type whose underlying type is a function.
Lastly, you're trying to assign a function with the signature func() (MyData, error) to the type Getter which is where you're getting the compile error I mentioned in the tl;dr. This compile error is because your type Getter specifies that any value of this type must be a function with zero input parameters and zero return parameters. You're trying to assign a function that does have return parameters.

Context value is nil when getting it with unexported struct key in Go HTTP handlers

Any help here is appreciated! I'm sure that I'm missing something really basic.
The problem I have is I am trying to get a value out of context in a demo web application, and I'm receiving the error:
2021/04/11 11:35:54 http: panic serving [::1]:60769: interface conversion: interface {} is nil, not []string
In my main function I'm setting the context with the following:
package main
type ctxKey struct{}
func someHttpHandleFunc() {
// .....
ctx := context.WithValue(r.Context, ctxKey{}, matches[1:])
route.handle(w, r.WithContext(ctx))
}
Then in my handler, I have the following:
package some_package
type ctxKey struct{}
func getField(r *http.Request, index int) string {
fields := r.Context().Value(ctxKey{}).([]string)
return fields[index]
}
I know that I'm missing something simple because if I try the above code and put my getField() function within the package main everything works.
For reference, this is a learning exercise, I'm trying to teach myself Go routing. I do know that there are routing packages available - but my goal is to learn. I'm trying my best to follow along with Different approaches to HTTP routing in Go. I have also read through Pitfalls of context values and how to avoid or mitigate them in Go. The latter seems to directly address the problem I'm having, but I can't seem to figure out how to solve it based on what is there.
Defined struct types defined in different packages are different.
package main
type ctxKey struct{}
is not the same type as
package some_package
type ctxKey struct{}
To make it intuitively clearer, think that if you were to reference these types from a third package — let's pretend the types were exported — you would have to import the corresponding package and use the appropriate selector:
package baz
import (
"myproject/foo"
"myproject/some_package"
)
func doBaz() {
foo := foo.CtxKey{}
bar := some_package.CtxKey{}
}
Now, the implementation of Value(key interface{}) uses the comparison operator == to determine whether the supplied key matches:
func (c *valueCtx) Value(key interface{}) interface{} {
if c.key == key {
return c.val
}
// then it checks if the key exists on the parent
return c.Context.Value(key)
}
Which implies that the types also must match. From the specs, comparison operators
In any comparison, the first operand must be assignable to the type of the second operand, or vice versa.
And clearly in your example ctxKey struct{} declared in main is not assignable to ctxKey struct{} declared in some_package, and vice-versa, because their types differ.
To solve your error, make sure the keys used when setting and getting the context value are the same type. The best way, also to ensure proper encapsulation, would probably be to set and get the context value from the same package:
package some_ctx_helper_pkg
// unexported, to ensure encapsulation
type ctxKey struct{}
func Set(ctx context.Context, value interface{}) context.Context {
return context.WithValue(ctx, ctxKey{}, value)
}
func Get(ctx context.Context) interface{} {
return ctx.Value(ctxKey{})
}

Golang microservice middleware allow any type but be strict on endpoint

New to golang but what I'm trying to do is make my logging middleware generic e.g. allow any type and then call the method for the next layer.
So below us the loggingmiddleware package, where I want to be able to accept any type and print it out.
package loggingmiddleware
import (
"context"
"time"
gokitlogger "github.com/go-kit/kit/log"
)
type layer interface {
Run(context.Context, interface{}) (interface{}, error)
}
type LoggingMiddleware struct {
Logger gokitlogger.Logger
Layer layer
}
func (mw LoggingMiddleware) Run(ctx context.Context, i interface{}) (output interface{}, err error) {
defer func(begin time.Time) {
mw.Logger.Log(
"method", "name of method",
"input", i,
"output", output,
"err", err,
"took", time.Since(begin),
)
}(time.Now())
output, err = mw.Layer.Run(ctx, i)
return
}
However I want to be strict when calling the next method, if it needs to be string I want to set the type to be string rather than interface{}
In my example I want to make sure only a float64 type will be used as an argument
type mathServiceInterface interface {
Run(context.Context, float64) (float64, error)
}
type mathService struct{}
func (mathService) Run(_ context.Context, f float64) (float64, error) {
return f * f, nil
}
However with my current implementation I'm getting this error...
# github.com/jakelacey2012/blankit/blankit-ms/sqaure
./main.go:92: cannot use ms (type mathServiceInterface) as type loggingmiddleware.layer in field value:
mathServiceInterface does not implement loggingmiddleware.layer (wrong type for Run method)
have Run(context.Context, float64) (float64, error)
want Run(context.Context, interface {}) (interface {}, error)
./main.go:92: cannot use loggingmiddleware.LoggingMiddleware literal (type loggingmiddleware.LoggingMiddleware) as type mathServiceInterface in assignment:
loggingmiddleware.LoggingMiddleware does not implement mathServiceInterface (wrong type for Run method)
have Run(context.Context, interface {}) (interface {}, error)
want Run(context.Context, float64) (float64, error)
I understand the error, however I don't know whether my implementation is over complicating things because I don't know go.
I hope what I'm saying makes sense, I was unsure what to title this as so please feel free to edit it.
Also if you need more code to better explain please do let me know.
What's going to be calling these? At some point there is an actual consumer, and that consumer will (presumably, based on your code) be using an interface (either layer or an identical interface). If there's middleware, that interface will necessarily be as generic as the middleware - i.e., taking a interface{} as a parameter to Run. So making something downstream more specific (besides not compiling as you've seen) doesn't make any sense: the actual consumer won't see the more-specific interface, it will see Run(Context,interface{}) (interface{},error).
The error message says it all, for a type to implement an interface its methods must exactly match the methods the interface defines.
Sadly, this means that your system won't work as designed. You will either need to use interface{} and assert to the actual type at the end point, or you will need a separate interface (and logger function) for each type.

Why json.Marshal and json.Unmarshal have different signature

func Marshal(v interface{}) ([]byte, error)
func Unmarshal(data []byte, v interface{}) error
notice that Marshal accept an interface and return a []byte as output, while Unmarshal accept an []byte and write output directly to the input parameter data
what make the difference in design
an associate question:
I think use input parameter could save in-memory copy once(function return need a copy), every assignment in golang is a copy op, so it looks like Unmarshal can save a copy, but Marshal not.
so I'm confused...
The potential functions in symmetric pairs are:
func Marshal(v interface{}) ([]byte, error) // 1
func Unmarshal(data []byte) (interface{}, error) // 2
func Marshal(v interface{}, data *[]byte) error // 3
func Unmarshal(data []byte, v interface{}) error // 4
Functions #3 and #4 might not look symmetric because the data argument to #3 is a pointer type and the v argument to #4 is not declared to be a pointer type. They are in fact symmetric because the actual value passed to the v argument of #4 must be a pointer. The function returns an error if it is not a pointer.
Function #2 is not viable because the function does not have access to the application's result type. The function cannot unmarshal the JSON to a type if the type is not known to the function. Further, the application will need to type assert the result to use it. The standard library avoids type assertions when possible.
Function #3 is not idiomatic. Pointers to slices are rarely used in Go. I don't recall the use of any in the standard library, but I am sure someone will leave a comment here if there is a one.
We are left with the unsymmetric pair of #1 and #4:
func Marshal(v interface{}) ([]byte, error) // 1
func Unmarshal(data []byte, interface{}) error // 4
By having Marshal return a byte array, you can easily pass that to functions like bufio.Writer.Write() which takes a byte array as an argument. You might have one of those when you're writing a web service that serves JSON.
If you're using Unmarshal, then you're likely reading from JSON. By having Unmarshal take the same first argument as Marshal returns, you can get any JSON that has been output by Marshal, and pass it straight to Unmarshal.

Resources