Skip to content

proposal: Go 2: introduce try/guard keyword for error handling #39890

Closed
@andig

Description

@andig

Go has suffered from seen a number of Go2 error handling proposals. I'm joining the chorus as I had the feeling that a comparatively less disruptive permutation of the discussion has not come up before. I'd be totally happy if this got closed right away, in that case sorry for wasting everybody's time.

Goals of this proposal:

  1. decrease verbosity, i.e. less typing and clearer structure of the non-error case
  2. full control over error handling, including annotating the error
  3. avoid hidden or non-local control flow magic

Before going to the details, I have considered:

Proposal

This is an adapted version of #33161 (comment), I couldn't find an original proposal to this case (/cc @carlmjohnson).

In short, I'm proposing to introduce a try keyword similar to Swift's guard that will be implemented as an error-checking and error-handling specific alternative to the existing if statement:

  • try must me followed by an assignment expression where the last assignment parameter must be an error and can be omitted
  • if the error parameter is omitted, a non-nil error (and other zero value or initialised variables) will be returned alongside with it. This does not require the error to be named in the API
  • if the try statement has a handler block, the block will be executed/ the error can be handled similar to if err != nil and returned or not. This typically requires the err parameter to be named.
  • unlike the if statement, a special scoping rule allows the assigned target variables (right term?) to escape the block (I understand this is similar to swift). This includes the error variable if it is named.

As such, this proposal is similar to #33161 (comment), taking the comments regarding control flow from #33161 (comment) into account.

Syntactically, the two following statements are equivalent:

try foo := bar()
try foo := bar() {
    return
}

It would look like

TryStmt = "try" AssignmentExpression [ Block ]

Examples

CopyFile

func CopyFile(src, dst string) error {
	try r := os.Open(src) // returns the error
	defer r.Close() // returns the error

	try w := os.Create(dst) // returns the error
	defer w.Close() // returns the error

	try io.Copy(w, r) // returns the error
	try w.Close() // returns the error
}

Update: Obviously this example needs an additional rule for error precedence during defer (tbd). Updated the defer Syntax. Using try inside deferred functions follows the same rules as everywhere else.

Hex

func main() {
	try hex, err := ioutil.ReadAll(os.Stdin) {
		log.Fatal(err)
	}

	try data := parseHexdump(string(hex)) {
		log.Fatal(err)
	}

	os.Stdout.Write(data)
}

Some thoughts

As @carlmjohnson said:

  • I think if something like this is done, it should be called guard because that's a name used by another language.
  • Probably it's not enough better than `if to be adopted.

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