В Go стандартный подход — обработка ошибок через возвращаемое значение error. Это позволяет явно контролировать, что делать при возникновении ошибки в каждой точке. Panic — механизм для обработки непредвиденных катастрофических ситуаций: она прерывает выполнение до ближайшего defer, где можно использовать recover для "спасения". Но recover работает только внутри defer.
Лучшие практики:
func safeDivide(a, b int) (int, error) { if b == 0 { return 0, errors.New("divide by zero") } return a/b, nil } func mustDivide(a, b int) int { if b == 0 { panic("divide by zero!") } return a/b }
Можно ли "поймать" любую паник ошибку с помощью recover в любом месте программы?
Ответ: Нет, recover сработает только внутри defer и только в той же горутине, где вызвана паника. В других случаях паника завершит выполнение этой (или всех) горутин — это часто становится неожиданностью.
История
В REST API использовали panic для обработки обычных ошибок в бизнес-логике. Это привело к неочевидным падениям приложения и некорректным логам, потому что recover корректно срабатывал не всегда.
История
В сервисе обработки платежей был реализован defer + recover в основной функции, чтобы "ловить" все паники, но забыли про горутины — в дочерних горутинах без defer/recover приложение падало с "страшными" ошибками, оставляя часть транзакций в неконсистентном состоянии.
История
В парсере сложных структур забыли сделать возврат ошибки, заменили его на panic — это усложнило сопровождение и тестирование, пришлось полностью переписывать обработку ошибок, чтобы иметь детальное логирование и корректный UX.