Reputation: 479
I am running into a common pattern where I have a function that takes user input, and either returns a successful output value or an error. But it can return different types of errors, some of which are the result of bad user input and others which are the result of internal errors (DB unavailable, for instance).
My functions have signatures that look like this:
// ProcessInput takes a user-input string and returns a processed value
func ProcessInput(input string) (ProcessedValue, error, error) {}
The first return value is meaningful (not nil) if no errors were encountered, the second return value is an error if the user input failed validation, and the third return value is an error if an unexpected internal error occurred.
This works fine but it does not feel very clean, and it's not obvious from looking at the signature what the different errors are. I considered naming the error returns but I don't like the side-effects of naming return parameters.
Are there any other patterns I could apply that are cleaner? Should I have a single error return and distinguish by type on the caller side?
Upvotes: 12
Views: 1744
Reputation: 3274
Should I have a single error return and distinguish by type on the caller side?
That's what I would recommend. You could create a global error variables for different types of errors. And caller can check what kind of error was returned.
var ErrValidation = fmt.Errorf("Validation failed.")
func ProcessInput(input string) (ProcessedValue, error) {
if !validate(input) {
return nil, ErrValidation
}
// process stuff and return
}
And on caller side:
value, err := ProcessInput(input)
if err != nil {
if err == ErrValidation {
// Tell user validation failed
} else {
// Show internal server error message
}
}
// do things with value
Upvotes: 2
Reputation:
Returning multiple error
does not seem very Go-like to me.
Why not have an interface called UserError
that defines a method that returns a message suitable to show the user. If the returned error does not implement UserError
, show a standard "Internal Server Error" message. Example:
type UserError interface {
error
UserError() string
}
type emptyInput struct {}
func (e emptyInput) Error() string {
return e.UserError()
}
func (emptyInput) UserError() string {
return "Empty input"
}
func ProcessInput(input string) (*ProcessedValue, error) {
if input == "" {
return nil, &emptyInput{}
}
}
func httpHandler() {
val, err := ProcessInput(input)
if err != nil {
if userErr := err.(UserError); userErr != nil {
// show userError.UserError() to user
} else {
// Log error
// show Internal server error message
}
}
}
Upvotes: 13