В последнее (и не только последнее) время ломают много копий по поводу неудобства обработки ошибок в Go.
Суть претензий сводится к тому, что прямое использование:
customVar, err := call()
if err != nil {
doSomething(err)
return err
}
на больших количествах повторений гораздо менее удобно, чем классическое:
try {
String customVar = call();
} catch (BadException e) {
doSomething(e);
sendException();
}
Можно долго спорить как по самому предмету претензий, так и по поводу обходных манёвров, однако же логика в «пакетном» подходе действительно имеется.
В связи с чем у меня и возникла мысль по поводу обработки исключений без особого отхода от «Go-way». Вариант не рабочий — всего лишь моя фантазия.
Выглядело бы это так:
try err {
customVar1, err := call1()
customVar2, err := call2()
customVar3, err := call3()
} catch {
doSomething(err)
return err
}
Общая идея такова: сразу после try объявляется переменная (в нашем случае err) типа error, область видимости которой — весь блок try...catch. Далее, при каждом присвоении переменной нового значения внутри блока try, компилятор проверяет его на nil, и если вернулась ошибка — следует переход в блок catch. Теоретически это не слишком затратная операция, производительность не должна пострадать.
Также возможно назначение нескольких переменных, типа:
try errIo, errNet {
customVarIo1, errIo := callIo1()
customVarIo2, errIo := callIo2()
customVarNet1, errNet := callNet1()
customVarNet2, errNet := callNet2()
} catch {
if errIo != nil {
doSomething(errIo)
return errIo
} else {
doSomething(errNet)
return errNet
}
}
В данном случае пример не слишком наглядный, однако же при большом количестве кода внутри try бывает полезно сгруппировать ошибки, чтобы в catch их обработка не сводилась к:
switch err {
case net.Error1:
doSomethingWithNetError()
case net.Error2:
doSomethingWithNetError()
case io.Error1:
doSomethingWithIoError()
case io.Error2:
doSomethingWithIoError()
}
В общем, у меня всё. Ругайте.