I have created a "base" repository struct for standalone and embedded use (e.g with CustomerRepository) to avoid having to check errors all the time, and to create an abstraction for Gorp (the database toolkit), and to create an API slightly more to my liking.
I check for errors in this base struct and panic if one is found, as if one does exist in my opinion it then indicates a dev error and the code may as well panic, seeing as validation etc. should happen before data gets to the Repository.
I found this question Go Error Handling Techniques, but it doesn't cover wrapping errors up in a base struct like I have done and just panicking.
Is what I have done idiomatic Go?
package repositories
import (
"github.com/coopernurse/gorp"
)
type Repository struct {
Gorp gorp.SqlExecutor
}
func (r *Repository) GetById(i interface{}, id int) interface{} {
obj, err := r.Gorp.Get(i, id)
if err != nil {
panic(err)
}
return obj
}
func (r *Repository) Get(holder interface{}, query string, args ...interface{}) interface{} {
if err := Gorp.SelectOne(holder, query, args); err != nil {
panic(err)
}
}
func (r *Repository) Select(i interface{}, query string, args ...interface{}) {
if _, err := Gorp.Select(holder, query, args); err != nil {
panic(err)
}
}
func (r *Repository) Insert(list ...interface{}) {
if err := r.Gorp.Insert(list...); err != nil {
panic(err)
}
}
func (r *Repository) Update(list ...interface{}) int64 {
count, err := r.Gorp.Update(list...)
if err != nil {
panic(err)
}
return count
}
func (r *Repository) Delete(list ...interface{}) int64 {
count, err := r.Gorp.Delete(list...)
if err != nil {
panic(err)
}
return count
}
if err != nil { return err } > is outweighed by the value of deliberately handling each failure condition at the point at which they occur. Key to this is the cultural value of handling each and every error explicitly.
Go's built-in errors don't contain stack traces, nor do they support conventional try / catch methods to handle them. Instead, errors in Go are just values returned by functions, and they can be treated in much the same way as any other datatype - leading to a surprisingly lightweight and simple design.
error is a built-in interface type in Go. An error variable represents any value that can describe itself as a string . The following is the error interface declaration: type error interface { Error() string.
Don't panic, this isn't the Go way. Instead, do something like this --
func (r *Repository) GetById(i interface{}, id int) (interface{}, error) {
obj, err := r.Gorp.Get(i, id)
if err != nil {
return nil, err
}
return obj, nil
}
Then just handle the error in your caller. I see from your comments above that you are using these functions inside a Martini handler, so you would do something like this --
func MyHandler(parameters) (int, string) {
obj, err := repository.GetById(something, id)
if err == repository.ErrNotFound {
return http.StatusNotFound, fmt.Sprintf("could not find by id: %d", id)
}
if err != nil {
return http.StatusInternalError, err.Error()
}
return http.StatusOk, fmt.Printf("obj: %v", obj)
}
This is more like the Go way. Make sure that r.Gorp.Get does return specific errors that you declare inside your package.
var ErrNotFound = errors.New("not found")
Create as many as make sense to your code.
The idiomatic way would be to return the error with the associated type value, i.e.
func (list ...interface{}) (v int46, err error) {}
... and subsequently check err != nil where these functions are called.
Ultimately using panic() will result in Exception-like error handling and more boiler-plate code (if you deem the error to be recoverable).
Idiomatic error handling is verbose in Go, but less so than emulating exceptions (which is fundamentally not the "Go way").
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With