GoSuda

Почему в Go отсутствует конструкция Try-Catch?

By Rabbit Princess
views ...

Go намеренно не поддерживает try-catch, а поддерживает только синтаксис panic-recover. Это вызывает недовольство многих разработчиков, привыкших к обработке ошибок с помощью try-catch. Итак, почему не используется try-catch? Это связано с тем, что try-catch имеет ряд проблем.

try-catch-finally

Конструкция try-catch предназначена для обработки ошибок и исключительных ситуаций, которые могут возникнуть во время выполнения программы (во время выполнения). Кроме того, конструкция finally содержит код, который должен выполняться безусловно, независимо от того, произошло исключение или нет.

Обработка ошибок и ответственность

В 80-х и 90-х годах обработка ошибок была очень простой. Сообщения об ошибках были только «дискета заполнена», «нет дискеты в дисководе», «нет прав на запись на дискету», и разработчик в те времена, если возникала ошибка, выбрасывал ее (throw) до точки обработки ошибок для общей обработки. В этой ситуации конструкция try-catch работала эффективно.

Однако со временем ситуация изменилась. Бизнес-логика усложнилась, появились базы данных и транзакции, нужно было интерпретировать множество сообщений запросов при вызове множества API через сеть, и даже с появлением параллельного программирования приходилось обрабатывать ошибки в других потоках, а не в основном.

Ошибки стали настолько сложными, что их больше нельзя было обрабатывать в одном месте, и никто не мог взять на себя ответственность за все ошибки в одном месте. Здесь возникает серьезная проблема с try-catch.

Перекладывание ответственности try-catch

try-catch, проще говоря, — это способ, которым субъект, вызвавший ошибку, перекладывает ответственность (завершение) за возникновение ошибки на кого-то другого. Этот объект перекладывания может быть конструкцией catch, родительским методом, родителем родителя родителя родителя... кем-то. Другими словами, в мире, где обработка ошибок становится все больше и сложнее, метод, выбранный try-catch, — это «кто-нибудь сделает». Посмотрите на код ниже.

1try {
2    data = readFile("hello.txt");
3    structuredData = parseData(data);
4    insertDBStatus(structuredData[1]);
5    startHTTPServer(structuredData[2]);
6} catch (Exception e) {
7    e.printStackTrace();
8}

Проблема с приведенным выше кодом заключается в том, что неясно, кто обрабатывает printStackTrace, и также невозможно узнать, в каком коде произошла ошибка. Более того, чем больше логики добавляется в оператор try, тем ужаснее становится проблема. Но, как это ни парадоксально, по мере усложнения разработки разработчики стали зависимы от конструкции try-catch, которая перекладывает ответственность, не задумывались об обработке ошибок, их чувство ответственности также ослабело, и в конечном итоге они забыли о сути обработки ошибок и исключений. Так как же golang решил эту проблему?

panic, recover

Одним из преимуществ Go является наличие множества систем, которые не заводят разработчиков на плохой путь, а делают их хорошими разработчиками. panic-recover также можно привести в качестве примера.

На первый взгляд, может показаться, что panic и recover ничем не отличаются от try-catch, но они отличаются тем, что при возникновении проблемы ответственность не перекладывается на внешний объект. Когда в Go возникает паника, вместо того, чтобы возвращать значение вовне, ее нужно решать в том месте, где она произошла. Это возлагает ответственность за обработку ошибок на разработчика и побуждает его более тщательно обдумать, где, кто и как должен обрабатывать эту ошибку. Это гарантирует пользователю автономию, но при этом оставляет место для размышлений.

Кроме того, выбор слова panic можно считать очень удачным, поскольку, в отличие от try-recover, panic-recover уже самим словом оказывает давление на разработчика, требуя использовать его только в случаях явных ошибок, а не исключительных ситуаций. Естественно, разработчик не злоупотребляет recover, а использует его только там, где это необходимо. Это очень помогает в написании лаконичного кода в Go.