Skip to content

Proposal: scoped error handling for named errors using with .. handle err ... #32795

Closed
@MoritzHamann

Description

@MoritzHamann

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    FrozenDueToAgeLanguageChangeSuggested changes to the Go languageProposalerror-handlingLanguage & library change proposals that are about error handling.v2An incompatible library change

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions