Создание своих ошибок

Создание своих ошибок в Go реализуется через определение пользовательского типа, который удовлетворяет встроенному интерфейсу error. Интерфейс error требует реализации метода Error, который возвращает строку с описанием ошибки. Пользовательский тип может хранить любые дополнительные поля, такие как код ошибки, временная метка или другие данные, что делает обработку ошибок более гибкой и информативной.

Создание кастомной ошибки через структуру

Самый распространённый способ создания своей ошибки — определение структуры, реализующей метод Error. В этом методе можно сформировать строковое представление ошибки, используя поля структуры. Такой подход позволяет передавать в ошибке множество параметров.

gopackage main

import (
  "fmt"
)

type MyError struct {
  Code  int
  Message string
}

func (e MyError) Error() string {
  return fmt.Sprintf("error %d: %s", e.Code, e.Message)
}

func doSomething() error {
  return MyError{Code: 404, Message: "not found"}
}

func main() {
  err := doSomething()
  if err != nil {
    fmt.Println(err)
  }
}

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

go"error 404: not found"

Использование указателя на структуру

Часто метод Error реализуют с использованием указателя на структуру (*MyError). Это позволяет избежать копирования структуры при вызове метода и обеспечивает согласованность, если структура содержит изменяемые поля или большие объёмы данных.

gopackage main

import (
  "fmt"
)

type CustomError struct {
  Code  int
  Message string
}

func (e *CustomError) Error() string {
  return fmt.Sprintf("custom error #%d: %s", e.Code, e.Message)
}

func process() error {
  return &CustomError{Code: 500, Message: "internal server error"}
}

func main() {
  err := process()
  if err != nil {
    fmt.Println(err)
  }
}

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

go"custom error #500: internal server error"

Использование функции-конструктора

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

gopackage main

import (
  "fmt"
  "time"
)

type AppError struct {
  Time  time.Time
  Code  int
  Message string
}

func (e AppError) Error() string {
  return fmt.Sprintf("[%s] %d: %s", e.Time.Format("2006-01-02 15:04:05"), e.Code, e.Message)
}

func NewAppError(code int, msg string) error {
  return AppError{
    Time:  time.Now(),
    Code:  code,
    Message: msg,
  }
}

func main() {
  err := NewAppError(403, "forbidden")
  if err != nil {
    fmt.Println(err)
  }
}

Результат выполнения кода (время будет отличаться):

go"[2026-09-04 12:00:00] 403: forbidden"

Сравнение кастомных ошибок

При необходимости сравнения кастомных ошибок рекомендуется использовать приведение типов (type assertion) или метод errors.As. Это позволяет получить доступ к полям структуры и выполнить нужные проверки, например, сравнить код ошибки.

gopackage main

import (
  "fmt"
  "errors"
)

type MyError struct {
  Code int
}

func (e MyError) Error() string {
  return fmt.Sprintf("code %d", e.Code)
}

func check(err error) bool {
  var myErr MyError
  if errors.As(err, &myErr) {
    return myErr.Code == 404
  }
  return false
}

func main() {
  err := MyError{Code: 404}
  fmt.Println(check(err))
}

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

go"true"

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

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