Description
I'm not sure if this is to late, since the try()
proposal seems nearly accepted. However, I would still to make proposal for error handling in Go2, using scoped error handling blocks for named errors.
Overview
The new keyword with
is used to define a block of code used as an error handler. This keyword must be followed by the keyword handle
and the name of the variable for which the error handler should be invoked in case of failure. The naming of the keywords or their order can obviously be changed.
Small example
with {
return "", err
}
handle err {
file, err := os.Open(filename)
defer file.Close()
content, err := ioutil.ReadAll(file)
return content, nil
}
Every time the value of err
is changed, it's value will be checked against nil and if it is not nil, the error handler will be invoked.
The example above could be transformed into current Go code like this:
file, err := os.Open(filename)
if err != nil { // the `err` variable is changed, check for nil
return "", err
}
defer file.Close()
content, err := ioutil.ReadAll(file)
if err != nil { // the `err` variable is changed, check for nil
return "", err
}
return string(content), nil
It is important to notice that it is still possible to only handle certain kind of failures with the error handlers, by using different error names:
with {
return "", err
}
handle genericError {
a, genericError := someFunctionA()
b, genericError := someFunctionB()
c, specificError := someFunctionC()
if specificError != nil {
// handle this error specificly
}
d, genericError := someFunctionD()
}
Nesting
Possibly the with...handle
blocks could be nested, allowing to execute multiple error handling to be executed.
The error handler would be executed in reverse order.
with {
return "", err
}
handle err {
a, err := someFunctionA()
b, err := someFunctionB()
with {
log.Printf(specificError)
}
handle err {
c, err := someFunctionC()
}
d, err := someFunctionD()
}
would be translated into:
a, err := someFunctionA()
if err != nil {
return "", err
}
b, err := someFunctionB()
if err != nil {
return "", err
}
c, err := someFunctionC()
if err != nil {
{
log.Printf(specificError)
}
{
return "", err
}
}
d, err := someFunctionD()
Implementation
This error handling would require two additional keywords to define the error handler (currenlty with
) and the block of code which should be checked (currently handle
). The implementation itself can be implemented as an AST transformation.
Every time the value of the name error is changed, a check against nil has to be inserted, and if successful, the error handler should be executed.
Drawback and advantages
For small functions which need a lot of different error handling, the resulting error checking code won't be smaller than in the existing solution via if
checks. However, for functions with very generic error handling (e.g data validation) which just return the encountered error, this solutions can greatly improve the readablity.