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
}
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").
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.