Is there a canonical way of passing options to a Go function? - go

I am writing a package that exposes this function:
func Marshal(input interface{}) ([]byte, error)
This is fine for the majority of situations, but I also want to prove another function if there are extra options:
type MarshalOptions struct {
OnlyStdClass bool
}
My first thought is to create another function:
func MarshalWithOptions(input interface{}, options MarshalOptions) ([]byte, error)
Is this the recommended way of doing this? Is there a standard naming convention for function that also provide a more specific version with options?

One common way of doing that is to declare the function as variadic so that it accepts zero or more options. Assuming Option is your option type, you would declare it like this:
func Marshal(input interface{}, options ...Option) ([]byte, error)
Then, within the function, options has the type []Option.
The function would then be called with zero or more Option arguments:
bytes, err := Marshal(input, Option1, Option2)
Or, if you have your options in a slice, you would call it like this:
bytes, err := Marshal(input, options...)
There are several references to this in the language spec (look for "variadic").

You can take a *MarshalOptions. The caller can then pass nil if they want the default behavior.
eg.
func Marshal(input interface{}, options *MarshalOptions) ([]byte, error)

I found this to the be the best balance between explicit and simplicity:
type MarshalOptions struct {
OnlyStdClass bool
}
// DefaultMarshalOptions will create a new instance of MarshalOptions with
// sensible defaults. See MarshalOptions for a full description of options.
func DefaultMarshalOptions() *MarshalOptions {
options := new(MarshalOptions)
options.OnlyStdClass = false
return options
}
func Marshal(input interface{}, options *MarshalOptions) ([]byte, error) {
// ...
}
Using the constructor pattern I can set reasonable defaults, without requiring that every option (especially if they may change) are explicitly set.
It's true that I could accept nil, but I don't because it makes it more explicit to read:
result := Marshal(123, DefaultMarshalOptions())

Related

idiomatic error and value order using Multiple Return Values in go

When creating a func that returns both an error and a result, which order should they be in? I've seen examples of both, and not sure where to look to confirm the idiomatic style.
should it be:
// a
func funcName(n int) (error, int) {}
// or b
func funcName(n int) (int, error) {}
As pointed out by #Gavin above in the comments :
If you look here, you will see By convention, errors are the last return value and have type error, a built-in interface.
As can be seen in the standard lib.

How to pass variable ids to statement.Query() in golang?

I have this query in postgres which queries 1 or n users based on the parameters passed:
select name, phone from clients where id in ('id1','id2')
Now when I try to use this at golang I'm having problems approaching how to pass this type of variable arguments to the statement.Query() function:
ids := []string{"0aa6c0c5-e44e-4187-b128-6ae4b2258df0", "606b0182-269f-469a-bb29-26da4fa0302b"}
rows, err := stmt.Query(ids...)
This throws error: Cannot use 'ids' (type []string) as type []interface{}
When I check in source code query it can receive many variables of type interface:
func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
return s.QueryContext(context.Background(), args...)
}
If I do this manually it works:
rows, err := stmt.Query("0aa6c0c5-e44e-4187-b128-6ae4b2258df0", "606b0182-269f-469a-bb29-26da4fa0302b")
But of course I need the args to be 1 or many more, and dynamically generated.
I'm using Sqlx lib.
As we can see on the Query() method scheme and also from the error message, the method requires an argument in []interface{} type.
func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
return s.QueryContext(context.Background(), args...)
}
In your code, the ids variable hold []string data. Change it to []interface{} so it'll meet Query() requirements, then it'll work.
ids := []interface{}{
"0aa6c0c5-e44e-4187-b128-6ae4b2258df0",
"606b0182-269f-469a-bb29-26da4fa0302b",
}
rows, err := stmt.Query(ids...)

How to modify GORM DB pointer in function?

I have a REST API application written in Go, which uses GORM as ORM. During refactoring of some parts, I wanted to move some common operations to an external function ApplyToDBQuery(query *gorm.DB), which takes a pointer to the DB query and modifies it, so that the modified query can be used later.
Example usage:
query = shared.DB.Debug()
req.ApplytoDBQuery(query)
if query.find(&data).Error != nil {...}
func (this *MyCustomRequest) ApplyToDBQuery(query *gorm.DB) {
query.Limit(...)
query.Offset(...)
query.Where(...)
}
I thought, that since I'm passing a pointer to the function, the original query should've been modified, but nothing really happened to the original query.
I've also tried passing a pointer to pointer ApplyToDBQuery(query **gorm.DB), returning the modified pointer ApplyToDBQuery(query *gorm.DB) *gorm.DB and out of lack of ideas, even a combination of these two - ApplyToDBQuery(query **gorm.DB) *gorm.DB
Gorm object clone itself for every operation, therefore the original pointed value is never changed.
You should return the latest version of gorm.DB:
return query.Limit(...).Offset(...).Where(...)
Change the method receiver to
func (this *MyCustomRequest) ApplyToDBQuery(query *gorm.DB) *gorm.DB {
return query.Limit(...).
Offset(...).
Where(...)
}
then use it as:
query = req.ApplytoDBQuery(query)
if query.find(&data).Error != nil {...}
The reason is already pointed by #R3v4n
You should go with what the previous two answers recommend, but, if for some reason, you have to apply changes to the passed in pointer, you can still do it "manually".
query = shared.DB.Debug()
req.ApplytoDBQuery(query)
if query.find(&data).Error != nil {...}
func (r *MyCustomRequest) ApplyToDBQuery(query *gorm.DB) {
q := query.Limit(...).Offset(...).Where(...)
*query = *q
}
As a side note, it is generally discouraged to use receiver names like this and self, instead the preferred way is to use a short, let's say 1 to 3 letters, abbreviation of the type's name.
For example:
func (r *Request) AddCookie(c *Cookie)
func (c *Client) Get(url string) (resp *Response, err error)
func (srv *Server) ListenAndServe() error
Complementing #R3v4n answer:
Use chaining
Use db.Scopes for code reusage instead of usual func calls.
It can be like this:
query = shared.DB.Debug()
if query.Scopes(req.ApplyToDBQuery).find(&data).Error != nil {
// handle error
}
func (this *MyCustomRequest) ApplyToDBQuery(query *gorm.DB) *gorm.DB {
return query.Where(...).Limit(...).Offset(...)
}

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.

Golang pass nil as optional argument to a function?

In Golang, http.NewRequest has a specification like this:
func NewRequest(method, urlStr string, body io.Reader) (*Request, error)
However, I can pass nil as the body option if I don't want to pass the body to an io.Reader object, like this:
req, err := http.NewRequest("GET", "http://www.blahblah.org", nil)
How do I implement this functionality in my code? I have a function that I want to pass an optional string value so that it can page through API results however if I pass a nil to the string input I get this:
./snippets.go:32: cannot convert nil to type string
The parameters for my function look like this:
func getChallenges(after string) ([]challenge, string, error)
Go does not have "optional" arguments as a generally understood concept in other languages; nil is just the zero value for an interface (io.Reader in this case).
The equivalent zero value for a string is an empty string:
getChallenges("")
If you want to accept 0 or more of the same argument type, you use the variadic syntax:
func getChallenges(after ...string) ([]challenge, string, error)
You can modify you function to receive pointer value, like this:
func getChallenges(after *string) ([]challenge, string, error)
Then you can pass nil as an argument to it. But don't forget to check after for nil value inside your function before dereferencing it, or you will get a nil pointer exception:
func getChallenges(after *string) ([]challenge, string, error) {
if after == nil {
// No value specified
} else {
fmt.Printf("After: %s\n", *after) // Note pointer dereferencing with "*"
}
// ...
}
Another option:
Just use two functions:
func getChallenges(after string) {}
func getAllChallenges() {
return getChallenges(/* some default value here */)
}
you can use ellipse operator to send the optional parameters.. don't pass anything in optional parameter and check the length of parameter.
it should solve your problem
func foo(params ...int) {
fmt.Println(len(params))
}
func main() {
foo()
foo(1)
foo(1,2,3)
}
Maybe wrap it in a struct?
type NilableString struct {
value string;
}
You can use reflect.
In fact io.Reader is a interface.
So you can define signature like func getChallenges(after interface{}) ([]challenge, string, error)
interface{} is a empty interface, that is interface for anything.
But I suggest you use syntax args... to pass slice , refer to fmt.Printf implementation for usage, because if you pass no string the slice len is 0 and
this will avoid the reflect which I think too heavy for your function.

Resources