Sentinel-ошибки
Sentinel-ошибки в Go - это предопределенные значения ошибок,
которые используются для идентификации конкретных ситуаций в программе.
Их принято объявлять как глобальные переменные на уровне пакета
с типом error. Названия таких переменных обычно начинаются
с префикса Err, например, ErrNotFound,
ErrInvalidInput. Эти ошибки служат маркерами,
по которым можно определить причину неудачи при выполнении операции.
Объявление Sentinel-ошибок
Для создания Sentinel-ошибки используется функция
errors.New, которая создает простую ошибку с заданным
текстовым сообщением. Объявлять такие ошибки следует как
экспортируемые переменные для использования в других пакетах.
package main
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("item not found")
var ErrInvalidInput = errors.New("invalid input provided")
func main() {
fmt.Println(ErrNotFound)
fmt.Println(ErrInvalidInput)
}
Результат выполнения кода:
"item not found"
"invalid input provided"
Использование Sentinel-ошибок
Sentinel-ошибки возвращаются функциями для указания на конкретную проблему. Это позволяет вызывающему коду точно определить причину ошибки и обработать её соответствующим образом.
package main
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("item not found")
findItem(id int) (string, error) {
if id < 0 {
return "", ErrNotFound
}
return "item value", nil
}
func main() {
res, err := findItem(-1)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(res)
}
Результат выполнения кода:
"item not found"
Сравнение с Sentinel-ошибками
Для проверки того, является ли ошибка конкретной
Sentinel-ошибкой, используется функция errors.Is.
Этот подход предпочтительнее прямого сравнения, так как
он корректно работает с обернутыми ошибками.
package main
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("item not found")
findItem(id int) (string, error) {
if id < 0 {
return "", ErrNotFound
}
return "item value", nil
}
func main() {
_, err := findItem(-1)
if errors.Is(err, ErrNotFound) {
fmt.Println("Item was not found")
} else if err != nil {
fmt.Println("Other error:", err)
}
}
Результат выполнения кода:
"Item was not found"
Обертка Sentinel-ошибок
Sentinel-ошибки могут быть обернуты в другие ошибки с
добавлением контекстной информации. Функция fmt.Errorf
с директивой %w позволяет создать обернутую ошибку,
сохраняя исходную для дальнейшей проверки через errors.Is.
package main
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("item not found")
getItem(id int) (string, error) {
if id < 0 {
return "", fmt.Errorf("getItem: id %d: %w", id, ErrNotFound)
}
return "item value", nil
}
func main() {
_, err := getItem(-1)
if errors.Is(err, ErrNotFound) {
fmt.Println("Original sentinel error detected")
}
fmt.Println(err)
}
Результат выполнения кода:
"Original sentinel error detected"
"getItem: id -1: item not found"
Рекомендации по использованию
При проектировании пакета рекомендуется объявлять все
Sentinel-ошибки в одном месте в начале файла. Имена
переменных должны начинаться с префикса Err и
быть экспортируемыми, если они предназначены для
использования за пределами пакета. Текстовые сообщения
ошибок должны быть понятными и однозначными.
package main
import (
"errors"
"fmt"
)
var (
ErrNotFound = errors.New("resource not found")
ErrInvalid = errors.New("invalid argument")
ErrPermission = errors.New("permission denied")
)
processAction(action string) error {
if action == "" {
return ErrInvalid
}
if action == "delete" {
return ErrPermission
}
return nil
}
func main() {
err := processAction("")
if errors.Is(err, ErrInvalid) {
fmt.Println("Action is empty")
}
err = processAction("delete")
if errors.Is(err, ErrPermission) {
fmt.Println("Cannot delete resource")
}
}
Результат выполнения кода:
"Action is empty"
"Cannot delete resource"
Смотрите также
-
функцию
errors.New,
которая создает базовую ошибку с текстовым сообщением -
функцию
errors.Is,
которая проверяет наличие конкретной ошибки в цепочке -
функцию
fmt.Errorf,
которая создает форматированную ошибку с возможностью обертывания -
механизм
обертывания ошибок,
позволяющий добавлять контекст к ошибкам