Linter that checks that all fields are explicitly set in struct initialisation - go

I want to have a static assert or linter for my go code that checks that I do not – by mistake – initialise the structs in my config file with default values for any field. I want everything to be set explicitly.
Is there a way to achieve this?
EDIT: Clarified question.

Check out the validator package.
https://gopkg.in/go-playground/validator.v9
We use it extensively in our code to validate structs.
e.g.
type Config struct {
Url string `validate:"required"`
MaxHops int `validate:"omitempty,min=0"`
MaxTerms int `validate:"omitempty,min=0"`
MaxCost float64 `validate:"omitempty,min=0"`
}
func Init(cfg *Config) error {
if err := validator.New().Struct(cfg); err != nil {
return errors.Wrap(err, "error in config")
}
//do something
}

You can use https://github.com/GaijinEntertainment/go-exhaustruct for it.
In golangci-lint, this linter is called exhaustruct and is disabled by default (see documentation).

Related

how to use vscode snippets to generate my own return code?

if I have a struct, like
type Person struct{
Name string
some fields here
}
how to generate code like this when I create a method
func (p *Person) foo() (Person,error){
...
return Person{},err
}
and do need some snippets like this when I want to catch some error
if err != nil {
return Person{}, err // some struct or []struct here
}
You could start using snippets in VS Code. Ensure "editor.tabCompletion" is enabled in settings.json (or use Ctrl+Space).
There are list of all snippets, you could add a new throug request for feature.
For example use "iferr" to generate error check. For sure it will not grab the name of your struct, but at least it will help you a bit :)
If it is ok for you to have snippet locally, just go to File -> Preferences -> User snippets. Choose GO language and insert yours.

Go: Access a struct's properties through an interface{}

I am having trouble in accessing the one struct's properties (named Params) in different file.
please consider x.go where i invoke a function(CreateTodo)
type Params struct {
Title string `json:"title"`
IsCompleted int `json:is_completed`
Status string `json:status`
}
var data = &Params{Title:"booking hotel", IsCompleted :0,Status:"not started"}
isCreated := todoModel.CreateTodo(data) // assume todoModel is imported
now CreateTodo is a method on a struct (named Todo) in different file lets say y.go
type Todo struct {
Id int `json:todo_id`
Title string `json:"title"`
IsCompleted int `json:is_completed`
Status string `json:status`
}
func (mytodo Todo)CreateTodo(data interface{}) bool{
// want to access the properties of data here
fmt.Println(data.Title)
return true
}
Now I just want to use properties of data in CreateTodo function in y.go.
But i am not able to do so and getting following error
data.Title undefined (type interface {} is interface with no methods)
I am sure issue is around accepting struct as an empty interface but i am not able to figure out.
Please help here.Thanks
So you have one of two options, depending on your model:
#1
Switch to data *Params instead of data interface{} as suggested in another answer but it looks like you are expecting different types in this function, if so; check option #2 below.
#2
Use Type switches as follows:
func (t Todo) CreateTodo(data interface{}) bool {
switch x := data.(type) {
case Params:
fmt.Println(x.Title)
return true
// Other expected types
default:
// Unexpected type
return false
}
}
P.S. Be careful with your json tags: it should be json:"tagName". Notice the ""! Check go vet.
You could just type the function parameter:
func (mytodo Todo)CreateTodo(data *Params) bool{
// want to access the properties of data here
fmt.Println(data.Title)
return true
}
See: https://play.golang.org/p/9N8ixBaSHdP
If you want to operate on a Params (or *Params), you must do that.
If you want to operate on an opaque type hidden behind an interface{}, you must do that.
In short, you cannot peek behind the curtain without peeking behind the curtain. Either expose the actual type Params, so that you can look at it, or keep all the code that does look at it elsewhere. The "keep the code elsewhere" is where interface really shines, because it allows you to declare that something otherwise-opaque has behaviors and ask for those behaviors to happen:
type Titler interface {
GetTitle() string
}
If Params has a GetTitle function, it becomes a Titler.
You can now define your CreateTodo as a function that takes a Titler, and then you can pass &data to this function.
This structure is overall quite klunky and it seems much more likely that Todo itself should be an embeddable struct instead, but see a more complete example starting from a stripped-down version of your sample code here, in the Go Playground.

Return error from deferred function when error is already returned

Update: I think now that there is no universal answer to this question. We can return both errors using the technique explained in the answer. I think that the most important thing here is not to forget the case when we have two errors and somehow handle it.
Notes: There are many questions on SO about how to return an error from deferred function. This is not a question here.
(In Go) What is the proper way to return an error from a deferred function when the function is already returning an error. For example
func errorMaker() (err error) {
defer func() {
err = errors.New("Deferred error")
}()
err = errors.New("Some error")
return
}
func main() {
err := errorMaker()
fmt.Printf("Error: %v\n", err)
}
In the code above the error returned by the deferred function overwrites the error returned by the function. What is the canonical way to return both errors? If another programmer uses my function what result might she expect from the function when the function returns 'two errors'?
Should I use Error wrapping for this?
Additional notes:
As #Volker says in his comment I write some application specific handling for this error. Because I know what should be done based on nature of the errors.
I think my question is - if I want to return all errors from the function what is the best way to combine them in my scenario?
Disclaimer: I don't know if the following advice can be seen as "standard" or "widely-accepted".
Should I use Error wrapping for this?
Short answer: yes (I would do so).
Go 1.12 and earlier
What I do when I need my errors to convey some specific meaning, without foregoing the error interface, I create a wrapper that implements the error interface - Error() string -. This wrapper contains all extra information I need.
If the caller is aware of the existence of those extra info, it can unwrap the error with a cast and find those info.
With the added benefit that unaware callers can just handle the error as a generic error.
type MyError struct {
DeferredError error
}
// Implements 'error' interface
func (e MyError) Error() string {
// format to string
}
func someFunc() error {
// might return an instance of MyError
}
...
// Caller code
err := someFunc()
if err != nil {
if myErr, ok := err.(*MyError); ok {
// here you can access the wrapped info
fmt.Println(myErr.DeferredError)
} else {
// otherwise handle the error generically
}
}
Go 1.13 onwards
With Go.13 you can use errors.As to unwrap an error. From the official docs:
[The method] As finds the first error in err's chain that matches target, and if so, sets target to that error value and returns true. The chain consists of err itself followed by the sequence of errors obtained by repeatedly calling Unwrap.
var myErr *MyError
if errors.As(err, &myErr) {
// here you can access the wrapped info
fmt.Println(myErr.DeferredError)
} else {
// otherwise handle the error generically
}
As the docs say the myErr variable is populated as a side-effect of calling As.

calling function from map[string]interface{} by giving key

I want to be able to pass function name to gin.Engine route handler. I have the following code;
// status service
type StatusService struct {
App *gin.Engine
}
func (s *StatusService) Ping(ctx *gin.Context) {
ctx.JSON(200, gin.H{
"message": "pong",
})
}
app := gin.Default()
// define services
statusService := &services.StatusService{
App: app,
}
ss := make(map[string]interface{})
ss["auth"] = statusService
app.GET("/ping", ss["auth"].Ping)
The compiler gives the following error;
./app.go:60: ss["auth"].Ping undefined (type interface {} has no field or method Ping)
Any ideas about how to fix that?
interface{} works for just about any type, the problem is that you've failed to assert which type the thing is. In this case you would need something like... ss["auth"].(*StatusService).Ping(myCtxInstance). This answer has a more thorough example which I'll refrain from duplicating; Go map of functions
Couple other things; if your real use case is as simple as your example just stop what you're doing and add func(ctx *gin.Context) as a second argument. Also, depending on the nature of the functions you want to use (like if they all have the same args and return types) then you might want to use a second arg for the delegates, map[string]func(argumentType) would be more appropriate.
The design you currently have pushes all errors to runtime which obviously is less desirable than the compile time safety you'd get from either of the options I touched on above.

Why embedded structs are not decoded when there is an Unmarshal implementation?

I have a type ServiceAccount which embeds two other types (User and Policy). However it seems the fields of the User type are totally ignored due the Unmarshaler implementation of the Policy type.
Is there any good reason for this behaviour? It looks like a bug to me because the json package can see through reflection that we have two types embedded and not only the type Policy.
I'm aware that I can "fix" the issue by implementing the Unmarshaler interface on type ServiceAccount too.
package main
import (
"encoding/json"
"fmt"
)
type ServiceAccount struct {
User
Policy
}
type User struct {
UserID string `json:"userID"`
}
type Policy struct {
Scopes string `json:"scopes,omitempty"`
}
// PolicyRaw is the Policy type as received from client.
type PolicyRaw struct {
Scopes string `json:"scopes,omitempty"`
}
func main() {
s := `{"userID":"xyz", "scopes":"some scopes"}`
srvAcc := &ServiceAccount{}
if err := json.Unmarshal([]byte(s), srvAcc); err != nil {
panic(err)
}
fmt.Printf("srvAcc %v", *srvAcc)
}
func (p *Policy) UnmarshalJSON(b []byte) error {
pr := new(PolicyRaw)
if err := json.Unmarshal(b, pr); err != nil {
return err
}
p.Scopes = pr.Scopes
return nil
}
Execute
I don't think it's a bug but just just how interfaces and embedding works. It just happens not to be what you want/expect here.
json.Unmarshal figures out to use the UnmarshalJSON method via this line:
if u, ok := v.Interface().(Unmarshaler); ok
As you know, something implements an interface if it has the right method set which *Policy and *ServiceAccount do. So it's expected that JSON decoding of the the outer type would just call the appropriate method and think it's done.
Interestingly, if you were to experiment and add a dummy method such as:
func (u *User) UnmarshalJSON([]byte) error {return errors.New("not impl")}
then although *User and *Policy would now both implement the interface,
*ServiceAccount will no longer implement that interface. The reason is clear if you try and explicitly call srvAcc.UnmarshalJSON which would then give a compiler error of "ambiguous selector srvAcc.UnmarshalJSON". Without such a call the code is legal and the method is just excluded from the method set.
So I think the solution is one of:
Just don't embed things that implement json.Unmarshaller if you want to marshal the result, e.g. use a named field instead.
Make sure you explicitly implement json.Unmarshaller for the outer type yourself when doing such embedding (e.g. add an UnmarshalJSON method to *ServiceAccount).
Make sure at least two embedded things implement json.Unmarshaller and then they'll work individually¹ but the "outer" type will get the default behaviour.
The last option seems like a hack to me. (And btw could be done on purpose with something like:
type ServiceAccount struct {
User
Policy
dummyMarshaller
}
type dummyMarshaller struct{}
func (dummyMarshaller) MarshalJSON([]byte) error {panic("ouch")}
but that looks really hacky to me).
See also:
https://golang.org/ref/spec#Struct_types
https://golang.org/doc/effective_go.html#embedding
¹ Further testing shows that decoding such anonymous (i.e. embedded fields), that their UnmarshalJSON methods do not get called.

Resources