An error variable is a package-scoped sentinel error representing a meaningful failure condition. A complex function can return different variables, allowing callers to distinguish cases such as timeouts and rejected requests without defining a custom error type for each one. section 4.1.3
Listing 4.8 Handling two different errors
package main
import (
"errors"
"fmt"
"math/rand"
"time"
)
const MAX_TIMEOUTS = 5
var ErrTimeout = errors.New("The request timed out") #1
var ErrRejected = errors.New("The request was rejected") #2
func init() {
rand.Seed(time.Now().UnixNano()) #3
}
func main() {
response, err := SendRequest("Hello") #4
if errors.Is(err, ErrTimeout) {
timeouts := 0
for err == errors.Is(err, ErrTimeout) { #5
timeouts++
fmt.Println("Timeout. Retrying.")
if timeouts == MAX_TIMEOUTS {
panic("too many timeouts!")
}
response, err = SendRequest("Hello")
}
}
if err != nil { #6
fmt.Println(err) #6
} else { #7
fmt.Println(response) #7
} #7
} #7
func SendRequest(req string) (string, error) { #8
switch rand.Intn(3) % 3 { #9
case 0: #10
return "Success", nil
case 1:
return "", ErrRejected
default:
return "", ErrTimeout #11
} #11
}
Defines ErrTimeout and ErrRejected with errors.New, returns them from a simulated request, detects ErrTimeout with errors.Is, retries, and reports the final result. section 4.1.3
The pattern supports practical error handling:
- Declare descriptive package-scoped variables such as ErrTimeout and ErrRejected. section 4.1.3
- Return the variable whenever its associated condition occurs, then let callers choose separate handling for each case. section 4.1.3
- Use errors.Is to recognize a target error, retry bounded timeout failures, and handle any remaining error. section 4.1.3
