LINUX.ORG.RU

Каскады iferr: как живете с ними?

 ,


2

3

Не секрет, что практически все функции в Golang возвращают ошибки. И тут два пути: или игнорировать или обрабатывать в iferr. Игнорировать почти никогда не получается, так что выбор невелик и код превращается в

val, err := SomeFunc()
if err != nil {
..do some..
}
. Да, это правильно, но это ж на каждый чих проверка получается. Нельзя ли сделать для этого условный defer или как-то иначе скрыть явную обработку ошибок? Может, кодогенерация поможет или еще что? Как вы пишете максимально чистый и компактный код?

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

Вот же в гугле дурачки работают! Вместо «чистой» и многословной джавы на лаконичный котлин пореползают под андроидом. Срочно расскажи им о том, что они все делают не так))

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

В смысле? Я с удовольствием программирую на Котлине, что вы от меня хотите, дорогой Пырн? Я вроде нигде не утверждал, что мне лично нужна абсолютная чистота кода, и даже нигде не говорил, что это благо. Мир построен на компромиссах.

FishHook ☕☕☕☕☕
()

За последние лет пять была пачка пропозалов, чтобы это дерьмо заDRYить, но вроде как ничего не вышло.

Как вы пишете максимально чистый и компактный код?

Был краткий опыт написания в прод небольшого сервиса на Go, и после этого я так решил для себя эту проблему: больше не пишу на Go.

theNamelessOne 👍
()
Ответ на: комментарий от FishHook

В смысле? … Чистый код всегда многословен.

Это конечно так. Но обратное не верно, многословный код не всегда чистый. И мне кажется, ТС хочет знать про это.

Мне даже стало интересно: https://go.dev/blog/error-handling-and-go

func viewRecord(w http.ResponseWriter, r *http.Request) *appError {
    c := appengine.NewContext(r)
    key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
    record := new(Record)
    if err := datastore.Get(c, key, record); err != nil {
        return &appError{err, "Record not found", 404}
    }
    if err := viewTemplate.Execute(w, record); err != nil {
        return &appError{err, "Can't display record", 500}
    }
    return nil
}

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

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

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

LongLiveUbuntu ☕☕
() автор топика
Ответ на: комментарий от PRN

Ну в том же Rust (где исключений тоже нет), эту проблему решили с помощью оператора ?/try.

В Go тоже когда-нибудь решат, наверно; вон даже недавно дженерики запилили и более-менее нормальную систему модулей)

theNamelessOne 👍
()
Ответ на: комментарий от LongLiveUbuntu

C древний как копролиты мамонта), насколько помню, его делали чтобы на асме 100500 версий одного и того же не писать. Про «местные» паттерны ничего не знаю, сорян, подсказать не могу. Разве что, пиши на котлине))

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

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

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

Смотря на последовательность операторов вы не видите где может вылететь исключение, а где нет.

Это IDE подсвечивает, стат.анализаторы есть. PHPDoc для этого тоже есть, https://docs.phpdoc.org/3.0/guide/references/phpdoc/tags/throws.html.

MOPKOBKA 👍👍👍👍👍
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)

В Rust есть unwrap, можешь тоже его сделать через препроцессор.

Либо сделать такое.

#define try(outvar, expr) outvar, err := expr; if err != nil { return nil, err }

И писать

try(val, SomeFunc())

Знаю что препроцессора нету встроенного, но можно просто вызывать /usr/bin/cpp (это препроцессор а не компилятор C++)

На Go не пишу, однако проблема кажется довольно простой.

MOPKOBKA 👍👍👍👍👍
()
Последнее исправление: MOPKOBKA (всего исправлений: 2)
Ответ на: комментарий от cobold

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

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

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

кстати, в гугле бьют по рукам за использование исключений https://google.github.io/styleguide/cppguide.html#Exceptions

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

то есть тебе не игнорирование надо, а лишь сахарок?

типа операторов ? и ! на конце, как в сделано в https://harelang.org/tutorials/introduction/#handling-errors

fn prompt() (void | error) = {
	fmt::println("Please enter a positive number:")!;
	const num = getnumber()?;
	fmt::printfln("{} + 2 is {}", num, num + 2)!;
};

такого в go нет

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

ЯП с исключениями это современный пых.

Ты обиделсо? Не плачь так))

Исключения - дерьмо с точки зрения понимания логики исполнения кода.

Только если не осилить исключения)

Смотря на последовательность операторов вы не видите где может вылететь исключение, а где нет.

В ЯП с исключениями как правило есть синтаксис определяющий кидается оно или нет. Документацию тоже никто не отменял.

А в го все правильно сделано.

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

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

Странно, у меня на обработку ошибок целые иерархии классов для кидания исключенй написаны. Видать я делал вид, что работа сделана, когда их писал)))

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

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

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

есть такой язычок как vlang - косит под go, но, как rust/python, не особо заботится о совместимости и открыт для ломающих экспериментов
обработка ошибок там выгдятит примерно так https://raw.githubusercontent.com/vlang/v/master/examples/errors.v

anonymous
()

вы либо ошибки проверяете, либо не проверяете. хозяин - барин.

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

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

возврат из функции кортежа (ну или тупла) из { result, error } - это просто синт. сахар, он ничто не меняет. чисто форма записи.

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

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

На самом деле в Go есть исключения (которые они называют паниками). И в целом несложно перевести всю обработку ошибок на них. С генериками можно даже красивые функции-обёртки написать.

Деструкторы для исключений не нужны. defer-блоков хватает.

Но это будет не каноничный код. Роб Пайк не одобрит.

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

избавиться от понимания

Наоборот, не унавоживать код тем, что не относится к флоу. Естественно исключения можно использовать так, чтобы всё запутать, но это не проблема исключений, а проблема говнокода. Его и без исключений легко можно наворотить.

ya-betmen 😊😊😊😊😊
()
Последнее исправление: ya-betmen (всего исправлений: 2)
Ответ на: комментарий от anonymous

как подсчитал? может в большинстве случаев подойдет вызов CheckErrors(err)

Никак не считал, исходил из эмпирического опыта: в реальных задачах с I/O большинство результатов всегда возвращают результат или ошибку (походы в БД, HTTP, прочие сокеты). Как в таких случаях CheckErrors(err) поможет? Ну вот например:

func MyFunc() (SomeResult, error) {
    // ...
    result, err := doSomethingUseful()
    CheckErrors(err)

    // ...
}

Ну а дальше-то что? Ты же не можешь просто так использовать result, потому как там может быть невалидное значение. И делегировать выбор, что делать в этой ситуации (например, вернуть nil, err из MyFunc), функции CheckErrors ты тоже не сможешь.

theNamelessOne 👍
()
Ответ на: комментарий от cobold

Да, я и говорю, избавиться от понимания того как может пойти flow программы. Это ж нахрен не надо.

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

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

возврат из функции кортежа (ну или тупла) из { result, error } - это просто синт. сахар, он ничто не меняет. чисто форма записи.

На практике меняет, особенно если как в rust есть еще сахар в виде оператора ‘?’. Кроме того возврат тупла позволяет образовывать цепочки вызовов и использовать монадические функции и писать в стиле функциональщины.

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

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

Есть куча языков с GC без полноценных деструкторов и с полноценными исключениями.

anonymous
()

Да, это правильно, но это ж на каждый чих проверка получается. Нельзя ли сделать для этого условный defer или как-то иначе скрыть явную обработку ошибок?

хмм, вот бы был механизм типа такого: мы оборачиваем завершающиеся с ошибкой методы в отдельный скоуп, и если ловим ошибку, то сразу выходим из этого скоупа и начинаем ее обрабатывать

try {
  SomeFunc1()
  SomeFunc2()
} catch (err) {
  ..do some..
}

может кто-нибудь такое уже придумал, есть у кого идеи? :)

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

может кто-нибудь такое уже придумал

плохая идея. Ошибка тут в SomeFunc1 или SomeFunc2? придется писать код, выясняющий где оно случилось или иметь разные типы ошибок и разные ветви catch.

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

alysnix ☕☕☕☕☕
()
Ответ на: комментарий от cobold

Смотря на последовательность операторов вы не видите где может вылететь исключение, а где нет

Плевать где оно вылетет. Где бы оно не вылетело, лови у корня, руби весь стек исполнения под корень, все равно данный стек уже дефектный, если возникла исключительная ситуация. Удобно дюже. В гошке такого нет, потому что ЯП принципиально минималистичный в плане наворотов. Это политика создателей такая

slew
()
Последнее исправление: slew (всего исправлений: 1)

Это такая политика создателей-ЯП должен быть максимально минималистичный и дубовый. Плюс без необходимости обработки исключений GC более произволительный по дизайну.

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

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

Ага, копипаста очень наглядна)) И мозг включать не надо, написал в десяти местах одно и тоже и все наглядно))

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

Если кому охота писать на Go как на java, то лучше сразу взять java. Каждый язык предполагает определённую философию, определённый стиль работы. И ему нужно следовать, либо страдать, либо выбрать другой язык. Стиль Go — максимально простой, даже дубовый код с минимумом неявной и непонятной фигни. Получается чутка многословно и неэлегантно, зато любой дурак, даже автор этого кода спустя полгода, легко его прочитает.

ugoday ☕☕☕☕☕
()
Ответ на: комментарий от PRN

Ага, копипаста очень наглядна))

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

вы знаете хоть, что делается при входе в блок try и выходе по исключению в один из блоков catch? какой там код будет дополнительный?

alysnix ☕☕☕☕☕
()
Ответ на: комментарий от ugoday

Если кому охота писать на Go как на java, то лучше сразу взять java.

Полностью с тобой согласен, но некоторые здесь пытаются доказать что исключения это ая-яй)), их использовать не надо. Очевидно, что у разных подходов свои плюсы и минусы.

PRN
()