Ответ
В Go принят подход явной обработки ошибок, в отличие от механизма исключений (exceptions) в других языках. Ошибка — это обычное значение, которое функция возвращает наравне с результатом. Это заставляет разработчика осознанно обрабатывать каждый возможный сбой.
Основной паттерн:
Функция, которая может завершиться с ошибкой, возвращает результат и ошибку как последний параметр (value, error). Вызывающий код обязан проверить, не является ли ошибка nil.
import (
"fmt"
"os"
)
f, err := os.Open("filename.txt")
if err != nil {
// Ошибка произошла, обрабатываем её
log.Printf("error opening file: %v", err)
return
}
defer f.Close()
// Ошибки нет, продолжаем работу с f
Ключевые концепции и инструменты:
-
Интерфейс
error: Любой тип, реализующий методError() string, удовлетворяет стандартному интерфейсуerror. Это позволяет создавать кастомные типы ошибок с дополнительной информацией. -
Создание ошибок:
errors.New("сообщение"): Создает простую ошибку с текстовым сообщением.fmt.Errorf("сообщение с форматом: %w", err): Создает ошибку с форматированием. Директива%wоборачивает (wraps) исходную ошибкуerr, сохраняя её в цепочке для дальнейшего анализа.
-
Проверка и извлечение ошибок (пакет
errors):errors.Is(err, target): Проверяет, есть ли в цепочке ошибокerrконкретная ошибкаtarget(например,io.EOF). Это предпочтительнее, чемerr == io.EOF, так как работает с обёрнутыми ошибками.errors.As(err, &target): Проверяет, есть ли в цепочке ошибка, которую можно присвоить переменнойtarget(например, кастомному типу ошибки), и в случае успеха извлекает её.
-
Кастомные типы ошибок: Позволяют переносить больше контекста, чем просто строка.
type MyError struct { Operation string Code int Err error } func (e *MyError) Error() string { return fmt.Sprintf("operation %s failed with code %d: %v", e.Operation, e.Code, e.Err) }
Ответ 18+ 🔞
А, Го, ёпта! Ну, слушай, тут у них, блядь, целая философия на ровном месте. Подход, сука, явной обработки ошибок. Это как в жизни, понимаешь? Не как у этих, блядь, изнеженных питонятников с их исключениями, которые летят нахуй непонятно куда и ломают всё. Тут ты, сука, сам за всё отвечаешь. Ошибка — это просто ещё одно значение, которое функция тебе, падла, возвращает. Как сдача в магазине. Получил — смотри, не обманули ли.
Основной прикол, на котором всё держится:
Функция, которая может накосячить, возвращает тебе два значения: результат и ошибку. И ошибка — это всегда последний аргумент, (value, error). И ты, мудак, обязан её проверить, а не делать вид, что её нет. Вот смотри, как это выглядит, в рот меня чих-пых:
import (
"fmt"
"os"
)
f, err := os.Open("filename.txt")
if err != nil {
// Опаньки, ошибка случилась, надо разбираться
log.Printf("error opening file: %v", err)
return
}
defer f.Close()
// Если добрались сюда — значит, всё чики-пуки, работаем дальше
А теперь, блядь, главные фишки, без которых нихуя не получится:
-
Интерфейс
error: Это ж гениально просто, ёба! Любой тип, у которого есть методError() string, — уже считается ошибкой. Можно свои ошибки городить, с блэкджеком и контекстом. -
Как ошибки рождаются:
errors.New("сообщение"): Банально, как пять копеек. Создал ошибку с текстом и пошёл дальше.fmt.Errorf("сообщение с форматом: %w", err): А вот это уже поинтереснее. Директива%w— это, сука, обёртка. Она берёт старую ошибкуerrи заворачивает в новую, как в одеяло. Потом можно будет докопаться, что там внутри было.
-
Как в этом дерьме копаться (пакет
errors— наш лучший друг):errors.Is(err, target): Ты смотришь, а не затесалась ли в цепочке ошибокerrконкретная сволочьtarget(ну, типаio.EOF). Это лучше, чем тупоerr == io.EOF, потому что работает даже если ошибку десять раз оборачивали.errors.As(err, &target): А это уже разведка боем. Ты проверяешь, а нельзя ли какую-нибудь ошибку из цепочки запихнуть в твою переменнуюtarget(например, в твой кастомный тип). Если можно — она туда самонаёбется.
-
Кастомные типы ошибок: Ну а если тебе мало просто текста, хочешь передать больше инфы — делай свою структуру, ёпта!
type MyError struct { Operation string Code int Err error } func (e *MyError) Error() string { return fmt.Sprintf("operation %s failed with code %d: %v", e.Operation, e.Code, e.Err) }Вот так вот, блядь. Теперь в ошибке лежит и операция, и код, и предыдущая ошибка. Красота, ебать мои старые костыли!