LINUX.ORG.RU

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

 , ,


0

3

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

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

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

★★★★★

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

Или это для вас так же монастыри, уставы и ересь?

Не надо путать соглашения с инженерными решениями.

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

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

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

В Go 2 будет тебе обработка ошибок

Есть мнение что это УГ. Теперь каждый вызов нужно заворачивать в check, а собственно обработка ошибки оторвана от места возникновения. Т.е. получаем такие кривые локальные ексепшны. Мне кажется лучше бы оставить все как есть, чем затыкать дыры в дизайне кривыми костылями. Интересно как они дженерики в синтаксис впихнут. Боюсь го2 совсем кособоким уродцем будет.

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

Но RazrFalcon-а этот вариант не устраивает.

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

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

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

Если нужны пруфы: запустите любую прогу на расте с RUST_BACKTRACE=1 и она замедлится в разы, так как будут сохранятся стекфреймы(?).

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

И вот еще интересно, что теперь делать буратинам, у которых полна коробочка имен handle и check в коде? Они же не были зарезервированы, так? В общем меня терзают смутные сомнения насчет адекватности разработчиков Go.

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

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

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

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

Ну кроме того, что можешь проматчить ошибку по типу.

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

Спасибо. Прямо сейчас некогда прочитать, но замедление в 10-20раз для исключений, и, что самое главное, если исключение не возникает, то код с исключениями быстрее, чем код с if error. Так что тут компромисс в обе стороны. Всё даже ещё более запущено, чем я думал сначала :) Кроме того, голанг (как потомок оберона) упирает на простоту, а не на быстроту. Поэтому высокая стоимость ошибок (которых в норме мало) вообще не должна кого-либо волновать.

den73 ★★★★★ ()
Последнее исправление: den73 (всего исправлений: 1)
Ответ на: faq смотрел? от pru-mike

faq смотрел?

Да там сплошная и прямая ЛОЖЬ написана. Почему я и говорю о секте. Секта - это там, где ложь. Поэтому это не то, что просто соглашения. Это когда вас пытаются обмануть и навязать решение на основе ложной информации. Например, о том, что panic/recover не требует дополнительных конструкций - это ложь. Здесь нужен defer, который в точности есть finally по поведению, просто код, выполняемый при раскрутке стека, расположен в другом месте - не в конце, а в начале. Делает ли это поток управления менее или более convoluted, чем finally? Вообще-то более естественно расположение finally как в Java и C# - управление идёт сверху вниз. defer, расположенный в точке инициализации, более удобный для чтения (захват и освобождение ресурса находятся рядом), но он более convoluted. Кроме того, без лямбд этот механизм вообще бы не работал. Т.е., если по честному, то механизм освобождения ресурсов при выходе устроен в голанге сложнее, чем в C++ или Java.

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

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

den73 познает мир

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

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

Есть мнение что это УГ. Теперь каждый вызов нужно заворачивать в check, а собственно обработка ошибки оторвана от места возникновения. Т.е. получаем такие кривые локальные ексепшны. Мне кажется лучше бы оставить все как есть, чем затыкать дыры в дизайне кривыми костылями. Интересно как они дженерики в синтаксис впихнут. Боюсь го2 совсем кособоким уродцем будет.

Соглашусь. Дженерики можно при необходимости реализовать через interface{}, а check будет дублировать частный случай паники. Это не по-обероновски. Хотя посмотрим, может быть они смогут изобрести нормальные дженерики.

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

Некорректно сравнивать плюсы

Можно вынести этот факт за скобки и считать деструкторы defer-ами. Т.е. померять стоимость самого механизма. Хотя судя по ссылке eao197 на SO, не так просто померять.

https://stackoverflow.com/questions/13835817/are-exceptions-in-c-really-slow

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

Разница минимальна. В панике error (убогий интерфейс с одним лишь методом), а в recover - просто «любое значение», которое можно и в строку превратить с помощью «%v», и тип его узнать. Т.е. по сути, кроме количества букв, никакой разницы нет. Ну и аномальное значение panic(nil), конечно.

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

работал в одно лицо и клал с прибором на предрассудки

Люди так не могут. В твоем случае ты опирался на свои личные предрассудки и «спорить» было не с кем=)

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

Разница минимальна. В панике error (убогий интерфейс с одним лишь методом), а в recover - просто «любое значение», которое можно и в строку превратить с помощью «%v», и тип его узнать. Т.е. по сути, кроме количества букв, никакой разницы нет. Ну и аномальное значение panic(nil), конечно.

ЯННП, ну да ладно.

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

Там есть очень похожие механизмы. Как минимум в обоих чётко видно, что вызываем метод, который может вернуть ошибку.

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

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

Есть какая-то специфика именно голанга?

Не знаю насчет специфики, но языки и компиляторы разные. Вот тебе бенч:

main.go

package main

import (
	"errors"
)

func doErr() error {
	return errors.New("err")
}

func doPanic() {
	panic("panic")
}

func hasErr() string {
	if err := doErr(); err != nil {
		return "Here's error"
	}
	return ""
}

func hasPanic() (res string) {
	defer func() {
		if r := recover(); r != nil {
			res = "Here's panic"
		}
	}()
	doPanic()
	return
}

func main() {
}

main_test.go

package main

import "testing"

func Benchmark_hasErr(b *testing.B) {
	for i := 0; i < b.N; i++ {
		hasErr()
	}
}

func Benchmark_hasPanic(b *testing.B) {
	for i := 0; i < b.N; i++ {
		hasPanic()
	}
}

Benchmark_hasErr-4 2000000000 0.35 ns/op 0 B/op 0 allocs/op

Benchmark_hasPanic-4 10000000 133 ns/op 0 B/op 0 allocs/op

паника в 380 раз медленнее на моей машинке

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

Это не религия, а этика, и образуется она неспроста.

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

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

В рантайме под нагрузкой

И это прекрасно.

Это ты не видел как течет память в библиотеках.

Еще один недостаток Го - в нем нет библиотек.

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

Юниттесты ортогональны компилятору

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

Да ладно? Смысла в Go тогда бы и вовсе не было.

Представь себе разработку оптимизирующего компилятора. У него 100500 флагов и 100500 оптимизаций. Нужно заставить работать все их сочетания. Оберон-вей - это сделать один, не оптимизирующий компилятор. За счёт простоты можно в сжатые сроки сделать его работающим правильно. Причём разница в трудоёмкости может комбинаторно зависеть от количества флагов и оптимизаций. Прекрасный пример оптимизирующего компилятора - это SBCL. Они постоянно в нём что-то меняют, и он постоянно сломан. LuaJit - второй пример.

С питоном сравнивать нельзя, т.к. это не компилятор вовсе.

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

Представь ситуацию, что тебе досталось легаси от «не такого как все» разраба

Ещё раз: err error никоим образом не защищает от помойки, потому что в статике невозможно узнать, какие ошибки могут прийти. Просто эта помойка ещё и разбавлена в эн раз шаблонным кодом, которого могло бы просто не быть.

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

Спасибо за замер. Дойдут руки - доделаю к нему happy path. Но тут ещё разница в том, что у тебя есть defer, которого нет в случае с err. В реальном коде defer будет в обоих вариантах, он будет закрывать, скажем, какой-нибудь файл. И обработка паники будет внутри этого defer. Т.е. пока это лишь один частный случай, полного анализа ситуации пока нет.

На самом деле, кроме скорости выполнения компьютера, который железный, есть ещё скорость диагностики ошибки, когда система падает в продакшене. Если в happy path паник нет, то резко облегчается локализация ошибки. Об этом я написал с самого начала темы.

Но во всяком случае, ясно, что скорость паники нужно учитывать при принятии решения.

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

Я ещё видел, но quicktemplate самый продвинутый, а дефолтный самый простой. Поискать, конечно, можно, но зачем? Факт в том, что стандартные батарейки покрывают большинство потребностей, а разрабов ещё не достаточно много, чтобы на каждый кейс было десяток альтернативных решений, да и дзен языка не особенно это подразумевает.

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

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

Да почти всё легаси такое. Тоталитаризм в виде модных методик с разглядыванием кода инквизиторами под лупой не так давно внедрили.

anonymous ()