Обработка ошибок является важным аспектом разработки программного обеспечения, и Go не является исключением. В этой статье серии я собираюсь показать вам обзор довольно уникального подхода Go к обработке ошибок.
В Go ошибки представлены типом error
, который представляет собой встроенный интерфейс с единственным методом: Error() string
. Этот метод возвращает строку с описанием ошибки. Важно отметить, что вы должны использовать значение nil
для обозначения отсутствия ошибки.
Теперь давайте посмотрим на пример того, как вы можете создать ошибку:
func div(x, y int) (int, error) { if y == 0 { return 0, fmt.Errorf("cannot divide by zero") } return x / y, nil }
Здесь функция div
принимает два целых числа и возвращает их частное. Если второй аргумент равен нулю, функция возвращает ошибку с сообщением 'нельзя делить на ноль'. В противном случае возвращается частное и ошибка nil
, указывающая на отсутствие ошибки.
Чтобы создать собственные ошибки, вы можете использовать функцию errors.New
в верхней части пакета, чтобы ошибка не создавалась каждый раз при вызове функции:
import ( "errors" ) errDivideByZero := errors.New("cannot divide by zero") func div(x, y int) (int, error) { if y == 0 { return 0, errDivideByZero } return x / y, nil }
Чтобы проверить наличие ошибок в Go, вы можете использовать оператор if
с переменной err
. Посмотрите на пример того, как вы можете использовать функцию div
:
result, err := div(10, 2) if err != nil { fmt.Println(err) return } fmt.Println(result)
Также часто можно увидеть обработку ошибок в Go с помощью ключевого слова defer
, которое позволяет вам отложить выполнение функции до тех пор, пока окружающая функция не вернет результат:
f, err := os.Open("file.txt") if err != nil { fmt.Println(err) return } defer f.Close()
В приведенном выше примере функция Open
вызывается для открытия файла 'file.txt'. Если есть ошибка, она выводится на экран, и функция возвращается. В противном случае файл открывается, и функция Close
откладывается до тех пор, пока не вернется окружающая функция. Это гарантирует, что файл всегда будет закрыт, даже если есть ошибка.
Есть также несколько других способов обработки ошибок в Go, например, использование функций panic
и recover
для обработки ошибок времени выполнения. Как и в большинстве случаев, панику следует использовать только в main.go
, рекомендуется изящная обработка ошибок.
Иногда вы можете захотеть обернуть ошибку дополнительным контекстом, прежде чем возвращать ее. Вы можете сделать это с помощью функции fmt.Errorf
, которая позволяет создать новую ошибку с отформатированной строкой. Например:
_, err := os.Stat("file.txt") if os.IsNotExist(err) { return fmt.Errorf("file not found: %w", err) }
В этом коротком фрагменте функция Stat
используется для проверки существования файла 'file.txt'. Если файл не существует, функция IsNotExist
возвращает true
, и возвращается новая ошибка с сообщением 'файл не найден' и исходная ошибка в качестве причины.
В некоторых случаях вы можете захотеть вернуть ошибку из функции, если она не может выполнить свою задачу из-за ошибки, возникшей при вызове другого метода. Вы можете сделать это с помощью функции errors.Wrap
, которая позволяет обернуть ошибку дополнительным контекстом:
_, err := os.Open("file.txt") if err != nil { return errors.Wrap(err, "failed to open file") }
Приведенный выше пример может быть полезен для предоставления большего контекста при отладке ошибки.
Вы можете создать свои собственные типы ошибок с дополнительными методами или полями, создав новый тип, реализующий интерфейс error
. Например:
type DivisionByZeroError struct { message string } func (e *DivisionByZeroError) Error() string { return e.message } func div(x, y int) (int, error) { if y == 0 { return 0, &DivisionByZeroError{"division by zero"} } return x / y, nil }
Здесь тип DivisionByZeroError
определяется полем message
и методом Error
, который возвращает сообщение об ошибке. Функция div
возвращает DivisionByZeroError
, если второй аргумент равен нулю, в противном случае она возвращает частное и ошибку nil
.
Переключатели типов могут быть полезны для обработки ошибок, которые имеют дополнительную информацию или настраиваемое поведение за пределами основного интерфейса error
. Они позволяют по-разному обрабатывать разные типы ошибок и предпринимать соответствующие действия в зависимости от конкретного типа error
:
func handleErrors(err error) { switch v := err.(type) { case MyError: fmt.Printf("MyError: %v\n", v) case *MyError: fmt.Printf("*MyError: %v\n", v) default: fmt.Printf("Other error: %v\n", v) } }
Здесь функция handleError()
принимает значение error
в качестве аргумента и использует переключатель типа для обработки различных типов ошибок. Тип MyError
— это настраиваемый тип ошибки, который реализует интерфейс error
. Переключатель типа обрабатывает как конкретный тип MyError
, так и тип указателя *MyError
. Если значение ошибки не относится ни к одному из этих типов, оно обрабатывается в случае default
.
Плюсы и минусы
В Go использование типа error
как первоклассного значения обеспечивает более гибкую обработку ошибок, чем языки, основанные на исключениях: модель обработки ошибок использует план на случай отказа. , а не успех». Кроме того, отсутствие исключений может упростить анализ поведения приложения Go, поскольку ясно, какие ошибки ожидаются и как они обрабатываются.
Однако есть и другая сторона медали, когда речь заходит о том, как реализована обработка ошибок в Go. Как упоминалось выше, в Go нет исключений, поэтому ошибки необходимо явно проверять и обрабатывать. Это может привести к повторной проверке ошибок, что сделает код менее удобным для сопровождения. Более того, тип error
является интерфейсом, поэтому он требует использования утверждений типа или переключателей типа для доступа к базовому значению ошибки. Это может быть довольно многословно.
Это обертка
Уникальная модель обработки ошибок Go широко обсуждается в инженерном сообществе, но, по моему мнению, явная природа обработки ошибок и отслеживание ошибок в первую очередь — это надежный способ повысить надежность. Приложения.
В следующей статье из серии Начало работы с Go мы рассмотрим тему параллелизма.
Вы можете узнать больше о Evendyne здесь.