Return a Bool or an Error in Go?
In Go, errors are returned instead of exceptions being thrown when something goes wrong. In other languages, you might use a boolean to indicate the success of an operation. This post captures some thoughts on when to return a boolean and when to return an error in Go.
Returning a Boolean
In Go, a get
operation on a map returns the value and a boolean indicating whether the key exists. Here's an example:
myMap := make(map[string]int)
val, ok := myMap["hi"]
fmt.Printf("val: %v\n", val)
fmt.Printf("ok: %v\n", ok)
// Output:
// val: 0
// ok: false
The value returned defaults to the "zero value" of the type (in this case, 0
for an int), and the boolean ok
indicates whether the key exists. This is particularly helpful because the map might contain the zero value as a valid entry. The ok
boolean allows us to differentiate between a missing key and a key that has the zero value.
Returning a Boolean in a Function
The above pattern is clean and useful, so it might seem natural to adapt it for functions that retrieve information. For example:
package main
import (
"fmt"
"net/url"
)
type Router struct {
Base string
}
func (r *Router) GetUrl() (string, bool) {
if len(r.Base) == 0 {
fmt.Println("Base URL is empty")
return "", false
}
parsedURL, err := url.Parse(r.Base)
if err != nil {
fmt.Println("Base URL is invalid")
return "", false
}
return parsedURL.String(), true
}
In this case, there could be multiple reasons why the function might not return a valid URL. Returning a boolean makes the function usable, but the caller loses the ability to understand why the operation failed.
Returning an Error
Here's an alternative approach where we return an error instead of a boolean:
package main
import (
"errors"
"net/url"
)
type Router struct {
Base string
}
func (r *Router) GetUrl() (string, error) {
if len(r.Base) == 0 {
return "", errors.New("base URL is empty")
}
parsedURL, err := url.Parse(r.Base)
if err != nil {
return "", errors.New("base URL is invalid")
}
return parsedURL.String(), nil
}
With this approach, the caller can handle the error as they see fit. For example, they can log the error, ignore it, or take specific actions based on the error's message. Additionally, the caller can still treat the error as a boolean by only checking whether it is nil
.
Summary
Returning an error provides more context than returning a boolean. While it allows users to treat the error like a boolean (by checking if it’s nil
), it also offers the flexibility to handle errors in a descriptive way.
Rule of Thumb
- If there is only one possible reason for failure, returning a boolean might suffice.
- If there are multiple reasons for failure, returning an error provides more context and control.