Sentinel-ошибки

Sentinel-ошибки в Go - это предопределенные значения ошибок, которые используются для идентификации конкретных ситуаций в программе. Их принято объявлять как глобальные переменные на уровне пакета с типом error. Названия таких переменных обычно начинаются с префикса Err, например, ErrNotFound, ErrInvalidInput. Эти ошибки служат маркерами, по которым можно определить причину неудачи при выполнении операции.

Объявление Sentinel-ошибок

Для создания Sentinel-ошибки используется функция errors.New, которая создает простую ошибку с заданным текстовым сообщением. Объявлять такие ошибки следует как экспортируемые переменные для использования в других пакетах.

gopackage 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)
}

Результат выполнения кода:

go"item not found"
"invalid input provided"

Использование Sentinel-ошибок

Sentinel-ошибки возвращаются функциями для указания на конкретную проблему. Это позволяет вызывающему коду точно определить причину ошибки и обработать её соответствующим образом.

gopackage 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)
}

Результат выполнения кода:

go"item not found"

Сравнение с Sentinel-ошибками

Для проверки того, является ли ошибка конкретной Sentinel-ошибкой, используется функция errors.Is. Этот подход предпочтительнее прямого сравнения, так как он корректно работает с обернутыми ошибками.

gopackage 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)
  }
}

Результат выполнения кода:

go"Item was not found"

Обертка Sentinel-ошибок

Sentinel-ошибки могут быть обернуты в другие ошибки с добавлением контекстной информации. Функция fmt.Errorf с директивой %w позволяет создать обернутую ошибку, сохраняя исходную для дальнейшей проверки через errors.Is.

gopackage 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)
}

Результат выполнения кода:

go"Original sentinel error detected"
"getItem: id -1: item not found"

Рекомендации по использованию

При проектировании пакета рекомендуется объявлять все Sentinel-ошибки в одном месте в начале файла. Имена переменных должны начинаться с префикса Err и быть экспортируемыми, если они предназначены для использования за пределами пакета. Текстовые сообщения ошибок должны быть понятными и однозначными.

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

Результат выполнения кода:

go"Action is empty"
"Cannot delete resource"

Смотрите также

  • функцию errors.New,
    которая создает базовую ошибку с текстовым сообщением
  • функцию errors.Is,
    которая проверяет наличие конкретной ошибки в цепочке
  • функцию fmt.Errorf,
    которая создает форматированную ошибку с возможностью обертывания
  • механизм обертывания ошибок,
    позволяющий добавлять контекст к ошибкам