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
()