Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: Go 2: handle statement to use a type switch expression #28344

Closed
nilslice opened this issue Oct 23, 2018 · 9 comments
Closed

proposal: Go 2: handle statement to use a type switch expression #28344

nilslice opened this issue Oct 23, 2018 · 9 comments
Labels
error-handling Language & library change proposals that are about error handling. FrozenDueToAge LanguageChange Proposal v2 A language change or incompatible library change
Milestone

Comments

@nilslice
Copy link

I would like to see if a modification to the proposed error handling design would be possible or is of any interest to other Go programmers. Just as switch can be parsed as a *ast.SwitchStmt as well as a *ast.TypeSwitchStmt, I suggest that the same hold true for the handle statement.

For example, the handle could optionally support:

func a() error {
    handle e := err.(type) {
    case *mypkg.ErrSpec:
        // log, react, etc
        return errors.Newf("spec error (%d): %v", e.SpecID, err)
    default:
       return err
    }

    check mypkg.SpecFunc()
    return nil
}

I think this would encourage more Go users to create useful error implementations, result in simpler debugging, and make it easier for programmers to write more reliable code.

It's clear that a type switch could be used inside the handle block, but I think the ergonomics of lifting the syntax up to the handle level work better, and make it more clear. If both a type switch and simpler error handling semantics are needed in the handle block, the user could simply use the handle statement as originally proposed, supporting both styles.

Apologies if this has already been proposed. I didn't find a duplicate suggested here or in the wiki.

@gopherbot gopherbot added this to the Proposal milestone Oct 23, 2018
@ianlancetaylor ianlancetaylor changed the title proposal: Go 2 handle statement to use a type switch expression proposal: Go 2: handle statement to use a type switch expression Oct 23, 2018
@ianlancetaylor ianlancetaylor added LanguageChange v2 A language change or incompatible library change labels Oct 23, 2018
@networkimprov
Copy link

Have you added a link to the feedback wiki?
https://github.com/golang/go/wiki/Go2ErrorHandlingFeedback

Note that many of its posts suggest various ways to invoke a handler by name, so I think it's likely we'll see named handlers in a future draft. Given that, different error types could be dispatched to separate handler blocks.

In Requirements to Consider for Go2 Error Handling, I assume named handlers, and mention implicit type assertion in B-3.1. That allows:

type T struct { m string, ... }
func (t *T) Error() string { return t.m }

handle err T { ... } // implicit err = input.(T)

@nilslice
Copy link
Author

nilslice commented Oct 25, 2018

Hi @networkimprov - I have not yet added it to the wiki, thank you for pointing that out. (Update: this proposal has been linked to the wiki)

handle err T { ... } // implicit err = input.(T)

I agree this would be nice to support, but I prefer to group them all in a single block, as opposed to the analog to your example in Go today, which would be doing a type assertion guarded by the "comma ok" in an if statement. It could be just as repetitive as if err != nil. Thank you for bringing this to my attention though as well!

@deanveloper
Copy link

deanveloper commented Oct 25, 2018

While this looks like a good idea, remember that errors.As is going to be coming in along with the check/handle, and this is not going to interop with that feature well...

@nilslice
Copy link
Author

@deanveloper - that's a good point, but I fail to see how using errors.As inside a possible case block could not be done as well to handle a chain with even more specificity. I agree that errors.As could provide the same effect as the type switch, but my point above around repetitive code still applies if limited to checking errors.As for each error type of interest.

@deanveloper
Copy link

Well it seems like the purpose of errors.As would be to replace if err, ok := err.(*foreign.Err); ok (aka case *foreign.Err:) with if err, ok := errors.As(*foreign.Err)(err) (under current proposal). Seems like adding this to the language would discourage using errors.As, which seems like a counter-intuitive thing to do

@nilslice
Copy link
Author

What would you rather write?

handle err.(type) {
    case *foreign.Err:
    // do something with *foreign.Err

    case *another.Err:
    // do something with *another.Err

    case *different.Err:
    // do something with *different.Err
}

or

if err, ok := errors.As(*foreign.Err)(err); ok {
    // do something with *foreign.Err
}

if err, ok := errors.As(*another.Err)(err); ok {
    // do something with *another.Err
}

if err, ok := errors.As(*different.Err)(err); ok {
    // do something with *different.Err
}

errors.As could and likely would be used within any of the above case statements, but to assert every different kind of error out of the interface using errors.As seems incredibly laborious to me.

@deanveloper
Copy link

What would you rather write ...

Those two things are completely different. It's not a "would you rather write", it's that the second one is just more correct most of the time. A switch statement doesn't scan down the error chain, it only looks at the topmost error. If we want error chaining to become a standard, adding features to only look at the outermost error is not a good idea.

@nilslice
Copy link
Author

I've said already in agreement with your desire to use errors.As as a further specificity check, however in practical real-world use, it's much more likely that a user will want to split cases by "top-level" error types, and then check down the chain when needed inside a case. But to limit users to errors.As every time they want to check an error this way seems no better than what we have today.

@ianlancetaylor
Copy link
Contributor

This seems like a small bit of syntactic sugar wrapper around the suggested handle statement. It seems to save naming the error variable, and a curly brace block. That might be fine if the great majority of handle statements were type switches, but I don't see any reason to think that that would be the case. I would expect that most handle statements would simply return the error or wrap it in another error.

Also, type switches on errors seem fragile. If some lower function adds a layer of wrapping, the type switch will stop working. Calling errors.As, on the other hand, would continue to work, but this proposal doesn't help with that case.

This is still linked from the wiki, but seems unlikely to be adopted, so closing.

@bradfitz bradfitz added the error-handling Language & library change proposals that are about error handling. label Oct 29, 2019
@golang golang locked and limited conversation to collaborators Oct 28, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
error-handling Language & library change proposals that are about error handling. FrozenDueToAge LanguageChange Proposal v2 A language change or incompatible library change
Projects
None yet
Development

No branches or pull requests

6 participants