Когда использовать os.Exit () и panic ()?

96

Может ли кто-нибудь объяснить ключевые различия между os.Exit()и panic()и то, как они используются на практике в Go?

Тимур Файзрахманов
источник
11
Просто комментарий, который, мы надеемся, поможет при чтении кода Go в будущем: во многих примерах кода panicон используется для выхода при ошибке исключительно из-за того, что его легко понять, и исключает импорт любых других пакетов. Это не значит, что это хорошо или идиоматическая практика! . Это просто устройство для экономии места, например, код. IRL резерв panicдля очень особых ситуаций.
Intermernet
2
Хм .. хорошо) особенно аббревиатура "IRL" - это для меня в новинку :) Не могли бы вы объяснить, как panic исключает импорт пакетов?
Тимур Файзрахманов
5
panicявляется встроенным. Рекомендуется (в зависимости от обстоятельств) использовать что-то вроде os.Exitи log.Fatalт. Д., Которые вернут код ошибки в ОС (всегда рекомендуется, если это возможно). Все это связано с импортом пакета и, таким образом, «загромождением» кода примера. Пример кода всегда следует использовать только для демонстрации решения конкретной проблемы. С кодом могут быть другие проблемы, которые при правильной демонстрации делают код более сложным и, следовательно, отвлекают от объяснения данного ответа. YMMV.
Intermernet
1
Хорошо, понял!) Большое спасибо) Вижу, есть еще одна аббревиатура для моего словаря :)
Тимур Файзрахманов
2
Н.П., рада помочь и пополнить ваш словарный запас аббревиатур :-)
Intermernet

Ответы:

86

Прежде всего, всякий раз, когда у вас возникает вопрос «как это используется на практике», хороший способ начать - поискать в исходном коде Go (или любой достаточно большой базе кода Go, на самом деле) и в документации пакета для ответов.

Теперь os.Exitи panicсовсем другие. panicиспользуется, когда программа или ее часть достигли неустранимого состояния.

Когда panicвызывается, в том числе неявно для ошибок времени выполнения, таких как индексирование фрагмента за пределами или сбой утверждения типа, он немедленно останавливает выполнение текущей функции и начинает раскручивание стека горутины, выполняя все отложенные функции по пути. Если эта раскрутка достигает вершины стека горутины, программа умирает.

os.Exitиспользуется, когда вам нужно немедленно прервать программу , без возможности восстановления или выполнения отложенного оператора очистки, а также вернуть код ошибки (который другие программы могут использовать для сообщения о том, что произошло). Это полезно в тестах, когда вы уже знаете, что после того, как один тест не прошел, другой тоже не сработает, поэтому вы можете просто выйти сейчас. Это также можно использовать, когда ваша программа сделала все, что нужно, и теперь нужно просто выйти, то есть после печати справочного сообщения.

В большинстве случаев вы не будете использовать panic( errorвместо этого вы должны вернуть ), и вам почти никогда не понадобится os.Exitза исключением некоторых случаев в тестах и ​​для быстрого завершения программы.

Айнар-Г
источник
9
«Это полезно в тестах, когда вы уже знаете, что после того, как один тест не пройдёт, другой тоже не сработает…» Это пахнет тестовым анти-шаблоном зависимых тестов. В хорошо написанном наборе тестов каждый тест независим; результат любого данного теста никогда не должен определять результат любого другого теста.
gotgenes
1
@gotgenes Не обязательно. Если у меня есть тест на то, что определенная функция возвращает структуру, отличную от nil, и этот тест завершается неудачно, то я могу ожидать, что все тесты, проверяющие значения структуры, тоже потерпят неудачу. Зависит код, а не тесты. (Тем не менее, я бы не стал использовать exitв этом случае, я просто ожидал бы большого количества неудавшихся утверждений.)
Дэвид
84

Прежде всего, os.Exit()его можно использовать для выхода из программы в обычном режиме без ошибок и без паники, так что это одно ключевое отличие. Другой - то, что панику где-то можно поймать и проигнорировать или зарегистрировать с помощью recover.

Но если мы говорим об ошибочном коде выхода, допустим:

Используйте, panicкогда что-то идет ужасно неправильно, возможно, это ошибка программиста, которую нужно было выявить до запуска в производство. Вот почему он печатает стопку.

Используйте os.Exit(errorCode)или что-то в этом роде, если хотите:

  1. контролировать код выхода программы для сценариев.

  2. требуется упорядоченный выход при ожидаемой ошибке (например, при ошибке ввода пользователя).

Так что в основном паника для вас, плохой код выхода для вашего пользователя.

Not_a_Golfer
источник
Спасибо очень полезно!)
Тимур Файзрахманов
14
«Так что в основном паника для вас, плохой код выхода - для вашего пользователя». <-
Отличный
1
Можно ли сказать, что panic () каким-то образом связана с обычным вызовом assert () в простом C? Что ж ... Я знаю, что всегда удаляю подтверждающий вызов перед запуском в производство, я включаю их только при тестировании новой функции. Я говорю, что большую часть времени я использую assert () для проверки инвариантов, которые, как я полагаю, должны выполняться в моем коде. Вы видите такое же использование для panic ()? :-)
yves Baumes
7

Ключевые отличия:

  1. os.Exit пропускает выполнение отложенной функции.
  2. С помощью os.Exitможно указать код выхода.
  3. panicзавершается, пока os.Exitнет. (Кажется, в других ответах об этом не упоминается.)

Если вам нужно выполнить отложенную функцию, у вас нет другого выбора panic. (С другой стороны, если вы хотите пропустить выполнение отложенной функции, используйте os.Exit.)

Если непустая функция определена таким образом:

  1. функция содержит много ветвей
  2. все ветки заканчиваются returnилиpanic

Тогда вы не можете заменить panicна, os.Exitиначе компилятор откажется компилировать программу, сказав «отсутствует возврат в конце функции». (Go здесь очень тупой, даже log.Panicне завершает функцию.)

При других условиях:

  1. Используйте, panicкогда что-то действительно связано, например, логическая ошибка программирования.
  2. Используйте, os.Exitесли вам нужен немедленный выход с указанным кодом выхода.
слабый
источник