LINUX.ORG.RU

golang - не хочу возвращать err, хочу паниковать!

 , , обработка ошибок


0

3

Какая-то секта с этими err. Код распухает в несколько раз. Идея с defer выглядит довольно здравой - я в своё время делал такой defer для 1C и для Delphi. Но паника лучше, чем возврат err-ов. Таковой возврат ничего не упрощает. Когда выпадает исключение, сразу виден весь стек. Сгенерированный err не показывает места своего возникновения, т.е. с помощью брекпойнтов нужно много итераций, чтобы локализовать ошибку. А на fatalpanic есть чуть ли не встроенный брекпойнт, во всяком случае, у меня на fatalpanic отладка сама по себе останавливается.

Кроме того, разбор err после каждого вызова офигенно многословен, код распухает буквально в разы.

Я собираюсь попробовать в своих упражнениях максимально использовать панику. Труъ голангисты, разубедите!

★★★★★
Ответ на: комментарий от xpahos

Это все хорошо в теории. На деле получается так, что на тестинге проверяются только те проблемные места, о которых ты знаешь.

Мир не идеален, но это не значит, что уже не имеет смысла к этому стремиться.

а для питона я не видел ничего подобного.

Сравнение тёплого с мягким.

mord0d ★★★★★
()
Ответ на: комментарий от RazrFalcon

В нормальной кодовой базе и утечек нет. В нормальном мире вообще всё хорошо. Только это не наш случай.

Нет, вы путаете нормальный мир с идеальным. В нормальном мире люди допускают ошибки. Но вот делать throw int в нормальной кодовой базе не будут. Индивиды, вроде ТС-а, которые везде рассуют своих тараканов есть, конечно, но от них нормальных кодовых баз не остается.

С чего бы это?

Потому что их там не будет.

eao197 ★★★★★
()
Ответ на: комментарий от den73

На самом деле просто не надо тратить своё мнение на чтение подобных религиозных трудов.

Nexmean
()

Кстати в Go2 обещают подвезти check.

fl, err := os.Open("path")
if err != nil {
    return err
}

превартится в

fl check := os.Open("path")
Где check - ключевое слово, которое делает
if err != nil {
   return [replies], err
}

kostyarin_ ★★
()
Ответ на: комментарий от kostyarin_

Оснавная причина, по которй используется проверка ошибок - это скорость.

Нет.

Да

Нет и ещё раз нет.

korvin_ ★★★★★
()
Последнее исправление: korvin_ (всего исправлений: 1)
Ответ на: комментарий от goingUp

человека, который пилил собственный ЯП

Если бы таких людей не было бы, то не было бы новых языков.

anonymous
()
Ответ на: комментарий от kostyarin_

Горазо быстрее проверить ошибку, или, например, число перед делением, чем обрабатывать исключение

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

Соответственно, в разных задачах будет эффективны разные подходы.

anonymous
()
Ответ на: комментарий от RazrFalcon

В нормальной кодовой базе и утечек нет

Некорректное сравнение. Утечку тяжело обнаружить на том же ревью кода, а вот выброс странного исключения - легко.

anonymous
()
Ответ на: комментарий от eao197

Но вот делать throw int в нормальной кодовой базе не будут.

На днях была новость об очередной уязвимости в ядре. Угадайте в чем была проблема? Оказывается кто-то, в хорошей кодовой базе, забыл задать NULL переменной. Чудеса.

RazrFalcon ★★★★★
()
Ответ на: комментарий от anonymous

выброс странного исключения - легко

С чего бы это? Если оно бросается раз в 100 лет - нет. И тесты тут не помогут.

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

На днях была новость об очередной уязвимости в ядре. Угадайте в чем была проблема? Оказывается кто-то, в хорошей кодовой базе, забыл задать NULL переменной. Чудеса.

Ядро линукс — хорошая кодовая база? Окай…

korvin_ ★★★★★
()
Ответ на: комментарий от RazrFalcon

Если вы отождествляете GNU-тый диалект C c С++, а забытое присваивание NULL с throw int, то у меня для вас плохие новости...

А нет, хорошие: для вас уже сделали Go и Rust. На чем-либо другом вы не сможете программировать, будет слишком сложно и непонятно.

eao197 ★★★★★
()
Ответ на: комментарий от anonymous

Получить скорость (для Go) лучшую чем проверка мне не удалось.

https://play.golang.org/p/kHUC0y7-iRa

Это скорее вопрос написания кода без заморочек. Т.к. на на ловлю и обёртку всех ошибок уходит ощутимое количество времени. Хотя если просто 'return err', то вроде не особо трудно. Сниппеты там и всё такое.

Думаю задержка recovera сильно увеличится, если recover расположен на дне стопки вызовов, а паника далеко сверху. Что собствено и является единственным возможным вариантом сокартить количество писанины касательно ошибок.

kostyarin_ ★★
()
Ответ на: комментарий от mersinvald

Обернув всё в rustc_test::black_box, получил:

test errors ... bench:          56 ns/iter (+/- 1)
test panics ... bench:       9,758 ns/iter (+/- 335)

То есть тоже самое.

RazrFalcon ★★★★★
()
Ответ на: комментарий от kostyarin_

Да любой дудосер обрадуется подобным новшевствам

Да, это тоже аргумент.

den73 ★★★★★
() автор топика
Ответ на: комментарий от RazrFalcon

С чего бы это? Если оно бросается раз в 100 лет - нет. И тесты тут не помогут.

А ревью кода поможет.

anonymous
()
Ответ на: комментарий от ya-betmen

Я имел в виду отделить деструкторы (содержание defer) от самой инфраструктуры, т.е. от собственно defer.

den73 ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Конечно, я клал с прибором только на чужие предрассудки, прежде всего, на «указания от гуру о том, как надо делать». Свои-то тараканы могли порезвиться. А чем плохо?

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Почему ЕДИНСТВЕННЫЙ профайлер недостаток?

dem ★★
()

Какая связь между «хочу много денег» и «компиляцию в машкод»? Почему тебя это волнует? Ты считаешь деньги за бинарь кошернее? Мудро конечно взять и по религиозным причинам урезать себе выбор до 1 (одной) технологии, а потом жаловаться на религиозное мышление окружающих.

anonymous
()
Ответ на: комментарий от RazrFalcon

Если оно бросается раз в 100 лет - нет. И тесты тут не помогут.

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

anonymous
()
Ответ на: комментарий от RazrFalcon

Да да, пусть хромает дальше в не консистентном состоянии. Или, изящно выходит при ошибке на 20ом уровне вложенности. То-то при разыменовании нулевого указателя errno взводится вместо сегфолта, не дураки ведь делали.

pon4ik ★★★★★
()
Ответ на: комментарий от anonymous

Баги все равно будут, как бы вы соломку не стелили.

Это не оправдание для того, чтобы использовать настолько откровенно ущербный язык, как C.

~~@~~

anonymous
()
Ответ на: комментарий от eao197

И какой в этом толк?

Что бы компилятором проверить что все возможные исключения обработаны.

Как узнать, что она вызывает внутри и что может вылететь наружу?

При сборке это видно компилятору, в с++ же как-то вывод типов сделали.

Leron ★★
()
Ответ на: комментарий от Leron

Что бы компилятором проверить что все возможные исключения обработаны.

Плюсую. Нужно.

den73 ★★★★★
() автор топика
Ответ на: комментарий от Leron

Что бы компилятором проверить что все возможные исключения обработаны.

Я так понимаю, что если у вас есть некий метод void f() throws * {...}, то сказанное вами относится не к нему, а к методам, в которых f() вызывается. Скажем вот здесь:

void g() {
  f();
  ...
}
компилятор укажет, что g() не перехватывает исключения, которые вылетают из f().

При том, что g() не знает, что может вылетать из f(), единственный вариант — это сделать в g() catch(Exception).

Ну так все тоже самое вы уже можете получить используя throws Exception.

При сборке это видно компилятору, в с++ же как-то вывод типов сделали.

Тогда вам придется ограничить контексты, в которых throws * может использоваться. В частности:

- запретить throws * для public-методов public-классов. В противном случае вам кто-то отдает jar с каким-то кодом версии 1.0.0, и там один набор исключений вылетает. Потом вы обновляетесь до версии 1.2.0 этого jar-а и сталкиваетесь уже с другим набором. Соответственно, вы начинаете зависеть от деталей реализации чужого кода, что есть очень плохо;

- запретить throws * у методов интерфейсов, поскольку тогда вам придется как-то бороться с тем, что разные реализации интерфейсов будут выбрасывать разные наборы исключений.

При этом ваш throws * не решает главную проблему RazrFalcon-а: не показывает, какие ошибки могут вылететь из метода.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Хм, в примере проверка исключений действительно в текущем виде работать не будет. Нужно добавить требование что бы у вызываемых функций была спецификация исключений.

запретить throws * для public-методов public-классов.

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

запретить throws * у методов интерфейсов

правда, видимо по этой причине checked exceptions сделаны так как сделаны.

Leron ★★
()
Последнее исправление: Leron (всего исправлений: 1)
Ответ на: комментарий от Leron

видимо по этой причине checked exceptions сделаны так как сделаны.

Мой опыт говорит о том, что checked exceptions — это отличная реализация красивой, в теории, идеи.

Но проблема в том, что теория и практика оказались разными вещами.

Объяснение этому нахожу в том, что на момент принятия ключевых решений по дизайну Java (а это конец 1980-х и самое начало 1990-х) не был накоплен еще достаточный опыт использования исключений в промышленных масштабах.

Если правильно помню, из более-менее известных на тот момент языков, исключения были в SmallTalk, Ada и Eiffel (но в Eiffel они сильно специфические, не такие, как в C++/Java/C#). Даже в C++ исключения были только описаны, но компиляторы с поддержкой исключений стали появляться только в начале 1990-х.

eao197 ★★★★★
()
Ответ на: комментарий от Leron

Исключения это не детали реализации а часть апи, такая же как типы и число аргументов у методов.

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

anonymous
()
Ответ на: комментарий от anonymous

То, что станет в будущем check - это то же, что ф-я без блока except, которая просто «прозрачна» для пролетающего в неё исключения. То же и функция, в которой не вызывается recover. К вопросу контролируемости исключений это, как мне кажется, ортогонально.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

просто «прозрачна» для пролетающего в неё исключения

Здесь ты расписал «условную последовательность действий», или «поток управления». Ты хочешь «метафункцию», которая явно оперирует с объектами-исключениями, но не хочешь его замечать/писать. При этом эта «метафунция» будет явно напоминать о себе, когда ты вылезешь на этот «метауровень», захочешь контролировать исключения, захочешь управлять потоком управления: например как в потоках, корутинах, межпроцессном взаимодействии и тп.

anonymous
()
Ответ на: комментарий от RazrFalcon

еня бесит отсутствие возможности посмотреть какие исключения бросает функция.

Особенности исключений в плюсах.
В жабке метод обязан объявить бросаемые исключения.
Кроме RuntimeException, но то что-то вроде panic.

WatchCat ★★★★★
()

Вместо того, чтобы разобраться объявлять все непонятное религией это так логично. Ссылки на то, что появляется второй неочевидный control flow уже были. Кроме того панику можно ловить исключительно в рамках одной горутины, и соответственно вся конкурентность ломается: https://play.golang.org/p/qEsA4dR0w_6

Панику можно использовать если у тебя исключительная ситуация, например отсутствует необходимая для работы сервиса зависимость во время инициализации (отсюда всякие MustGetBlah), или есть глубоковложенные циклы. Но паники должны на верхнем уровне ловится и превращаться в ошибку: https://golang.org/src/encoding/gob/decode.go?h=catchError#L1086 https://golang.org/src/encoding/gob/error.go?h=catchError#L34

Еще у многих есть привычка просто выбрасывать ошибку наверх вместо обработки. «Errors are values» как раз про это - https://golang.org/src/os/error.go Ошибки это тоже данные и с ними нужно работать как и с другими данными - https://dave.cheney.net/practical-go/presentations/qcon-china.html#_error_han...

feofan ★★★★★
()
Ответ на: комментарий от anonymous

удобный оператор ?

И в каком это месте он удобный, если с его помошью нельзя вернуть чужую ошибку (когда реализую трейт надо) и нельзя дописать в ошибку инфу из контекста (например имя файла, который не получилось прочитать). Приходится map_err перед вопросом добавлять. У монад, в отличии от этого макроса, более широкая область применения.

q0tw4 ★★★★
()

Есть кстати один случай, когда возвращать ошибку стопроцентно не нужно. А именно когда уверен, что ее там быть не может. Например если положил элемент в массив и следующей же строкой вытащил первый или последний элемент массива. Более того, есть смысл использовать ассерты, которые добавляют вариантов с паниками в код на фазе разработки, потому что вовремя упавшая программа лучше неправильно работающей (да и как обработать проблему, причиной которой является инвалидность самого кода программы, не автофиксом же кода). Причем иногда это верно даже для релиза. Разве что в задаче требуется корректно завершится, например аварийно посадить беспилотник...

q0tw4 ★★★★
()
Ответ на: комментарий от feofan

Вместо того, чтобы разобраться объявлять все непонятное религией это так логично.

Было не так. Я разобрался, увидел, что рекомендации ведут к неэффективности, и уже только после этого объявил их сектой. Не путаем религию и секту. По модулю идеологической борьбы, если взять в чистом виде, то секта разрушает (например, она может выступать против семьи и за аборты, что ведёт к гибели народа), а религия может быть и на пользу (допустим, она за семьи и за завоевательные войны, что ведёт к экспансии народа).

Но паники должны на верхнем уровне ловится и превращаться в ошибку

Разве я это отрицал? Это очевидно, вроде. Если применять их шире, то это правило обобщается до «панику нужно ловить на соответствующем уровне и либо устранять её последствия, либо превращать в ошибку».

Еще у многих есть привычка просто выбрасывать ошибку наверх вместо обработки

Так это ортогонально к способу обработки:

func callDangerous() (err error) {
 err = dangerous()
 return }
Это ровно то же самое, как в языке с исключениями написать
func callDangerous() throws {
 dangerous()
}
«Errors are values» - здесь нет особой инновации. В любом ЯП со сборкой мусора логично сделать исключение объектом. То, что этот объект бросают, не должно создавать принципиальной разницы. Во всяком случае, в лиспе throw работает именно так - оно передаёт обычный объект вглубь стека. Про С++ не особо помню, там может быть нюанс со временем жизни исключения, но в целом, объект исключения - это тоже value, и в C++, и в Java. Объявлять отсутствующую инновацию присутствующей - это грубая ложь.

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.