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: spec: handle errors with select #67316

Closed
2 of 4 tasks
chad-bekmezian-snap opened this issue May 11, 2024 · 27 comments
Closed
2 of 4 tasks

proposal: spec: handle errors with select #67316

chad-bekmezian-snap opened this issue May 11, 2024 · 27 comments
Labels
error-handling Language & library change proposals that are about error handling. LanguageChange Suggested changes to the Go language LanguageChangeReview Discussed by language change review committee Proposal Proposal-FinalCommentPeriod
Milestone

Comments

@chad-bekmezian-snap
Copy link

chad-bekmezian-snap commented May 11, 2024

Go Programming Experience

Intermediate

Other Languages Experience

Go, Python, JS, C#, Java

Related Idea

  • Has this idea, or one like it, been proposed before?
  • Does this affect error handling?
  • Is this about generics?
  • Is this change backward compatible? Breaking the Go 1 compatibility guarantee is a large cost and requires a large benefit

Has this idea, or one like it, been proposed before?

There have been some that are probably similar, but I don't believe there have been any quite like this

Does this affect error handling?

Yes. It is different than other error handling proposal because it doesn't really cut down a ton on the "make error handling take less lines of code", but I do think it improves error handling.

Is this about generics?

No

Proposal

There have been many language changes proposed to improve error handling. Many of these have had syntax changes to hopefully get rid of the so frequently used

if err != nil {
   return err
}

by adding some special syntax to quickly return. Such as err?. There have been a few common issues risen with these proposals:

  1. Doesn't fit into the way Go looks/feels today
  2. Adds a special keyword, and therefore isn't backwards compatible
  3. The proposal only cuts down on lines of code and does nothing to increase the likelihood of properly handling errors.
  4. A change of control flow, or at least on obfuscated control flow.

With this proposal, I aim to avoid all 4 of the above pitfalls. The proposal has three primary parts, all involving the enhancement of select statements. In my opinion, one of the issues with error handling in Go is that it is strictly a package level feature, not actually a language feature. This proposal would change that.

I propose the select statement be expanded to accept one parameter which, if provided, must be a nil or non-nil value of a type which implements the error interface. The select statement block is only entered if the error argument is non-nil. Its body syntax will be essentially identical to that of the switch statement, but behavior is a bit different for errors. Each case can be one of two format, except the default case which will have the same behavior and syntax as you would typically expect. The syntax for a case would either be case ErrIsValue: // case body or case varName as typedErr where as is a new keyword, but only recognized in this specific context, and therefore still backwards compatible. Each case is evaluated in the order it is provided. If the as keyword is absent, the behavior of matching the case is equivalent to checking whether errors.Is(err, ErrIsValue) == true. Similarly, if the as keyword is present, it is functionally equivalent to errors.As(err, &typedErr{})

Additionally, I propose a special "abbreviated" select syntax for errors that looks like this select err return "template string: %w", which behaves the same as select err { default: return fmt.Errorf("detailed message: %w", err) }.

Okay, that's enough talk with no examples! Here's what it looks like before and after the change:

Before Option 1

func handler(w http.ResponseWriter, r *http.Request) {
   if err := validateHeaders(r.Header); err != nil {
      if errors.Is(err, ErrUnprocessableEntity) {
         w.WriteHeader(http.StatusUnprocessableEntity)
         return
      }
     
     var badRequestErr InvalidFieldsErr
     if errors.As(err, &badRequestErr) {
        slog.Error("invalid headers", "fields", badRequestErr.Fields())
        w.WriteHeader(http.StatusBadRequest)
        return
    }

    w.WriteHeader(http.StatusInternalServerError)
    return
  }

  // handle happy path
}

func validateHeaders(header http.Header) error {
   for key, values := range header {
      if err := validateHeader(key, values); err != nil {
         return fmt.Errorf("specific error message: %w", err)
      }
   }
}

Before Option 2

func handler(w http.ResponseWriter, r *http.Request) {
   if err := validateHeaders(r.Header); err != nil {
      var badRequestErr InvalidFieldsErr
      switch {
      case errors.Is(err, ErrUnprocessableEntity):
         w.WriteHeader(http.StatusUnprocessableEntity)
         return
      case errors.As(err, &badRequestErr):
         slog.Error("invalid headers", "fields", badRequestErr.Fields())
         w.WriteHeader(http.StatusBadRequest)
          return
      }
 
    w.WriteHeader(http.StatusInternalServerError)
    return
  }

  // handle happy path
}

func validateHeaders(header http.Header) error {
   for key, values := range header {
      if err := validateHeader(key, values); err != nil {
         return fmt.Errorf("specific error message: %w", err)
      }
   }
}

After

func handler(w http.ResponseWriter, r *http.Request) {
   err := validateHeaders(r.Header)
   select err {
   case ErrUnprocessableEntity:
      w.WriteHeader(http.StatusUnprocessableEntity)
      return
   case badRequestErr as InvalidFieldsErr:
      slog.Error("invalid headers", "fields", badRequestErr.Fields())
      w.WriteHeader(http.StatusBadRequest)
      return
   default:
      w.WriteHeader(http.StatusInternalServerError)
      return
   }

  // handle happy path
}

func validateHeaders(header http.Header) error {
   for key, values := range header {
      err := validateHeader(key, values)
      select err return "specific error message: %w"

     // Alternatively
    /// select err := validateHeader(key, values); err return "specific error message: %w"
   }
}

It can be argued that "Before Option 2" and "After" are not all that different. However, I think the code paths and cases are far easier in my proposal as each case is very simple and clear. Additionally, this proposal does more to make errors and error handling first class citizens in the Go language, rather than merely a package level feature with a built in interface. I believe cutting out the boiler plate will lead to people reaching more often to handle errors properly. Additionally, this proposal removes the need for if err != nil {} that bothers people so often, and provides a common syntax for handling errors in all cases.

Is this change backward compatible?

I believe it is!

Cost Description

Implementation is probably somewhat costly. Runtime cost should be no different than what we have today.

Performance Costs

Compile time cost should be negligible. Runtime cost should be no different than today.

@chad-bekmezian-snap chad-bekmezian-snap added LanguageChange Suggested changes to the Go language Proposal v2 An incompatible library change labels May 11, 2024
@gopherbot gopherbot added this to the Proposal milestone May 11, 2024
@gopherbot gopherbot added the error-handling Language & library change proposals that are about error handling. label May 11, 2024
@golang golang deleted a comment from chad-bekmezian-snap May 11, 2024
@thediveo
Copy link

thediveo commented May 11, 2024

In "Before Option 2" example, in the switch case clauses, I think the returns are missing?

Other than this very minor issue: this seems to rather tackle mainly ErrorAs? Comparing "Before Option 2" and "After" I don't see much difference, except for the var statement necessary due to ErrorAs?

@seankhliao
Copy link
Member

The select statement block is only entered if the error argument is non-nil.

I believe this would break a large amount of existing code for very little appreciable gain.

@chad-bekmezian-snap
Copy link
Author

In "Before Option 2" example, in the switch case clauses, I think the returns are missing?

Other than this very minor issue: this seems to rather tackle mainly ErrorAs? Comparing "Before Option 2" and "After" I don't see much difference, except for the var statement necessary due to ErrorAs?

Added the returns.

This change cuts down in verbosity in three key areas, making error handling easier to read and simpler to write.

  1. ʼif err != nilʼ pretty much never needs to be written again, removing clutter.
  2. The cognitive load to comprehend the functional equivalent of errors.Is and error.As becomes significantly less as the reader has less boiler plate.
  3. Checking for specific errors becomes less verbose and repetitive, which I think will result in better error handling.

@chad-bekmezian-snap
Copy link
Author

The select statement block is only entered if the error argument is non-nil.

I believe this would break a large amount of existing code for very little appreciable gain.

@seankhliao please expound on how this would break existing code. I believe this is an entirely backwards compatible change.

@thediveo
Copy link

The improvement looks small, especially when you deal with lots of "library" code that sits somewhere inbetween of the whole stack, where other proposals were trying to improve: the if err != nil return fmt.Errorf pattern. Given your own highly appreciated examples, this slightly reduces the ErrorAs boilerplate, as the explicit var declarations are becoming implicit. It is probably difficult to have statistical data here, so my own experience is just a single, subjective data point: I rarely have need for ErrorAs, and in those situations I world not benefit from this proposal. But as I stress, my very subjective experience.

@chad-bekmezian-snap
Copy link
Author

The improvement looks small, especially when you deal with lots of "library" code that sits somewhere inbetween of the whole stack, where other proposals were trying to improve: the if err != nil return fmt.Errorf pattern. Given your own highly appreciated examples, this slightly reduces the ErrorAs boilerplate, as the explicit var declarations are becoming implicit. It is probably difficult to have statistical data here, so my own experience is just a single, subjective data point: I rarely have need for ErrorAs, and in those situations I world not benefit from this proposal. But as I stress, my very subjective experience.

@thediveo

It makes error handling much cleaner imo. Also, I updated the examples to better demonstrate the fmt.Errorf pattern and my solution there.

@seankhliao
Copy link
Member

This would no longer work:

func main() {
	var err error
	switch err {
	default:
		fmt.Println("happy path")
	}
}

@chad-bekmezian-snap
Copy link
Author

This would no longer work:

func main() {
	var err error
	switch err {
	default:
		fmt.Println("happy path")
	}
}

@seankhliao that’s a switch statement. My proposal involves select statements.

@seankhliao
Copy link
Member

sekect is currently defined as:

A "select" statement chooses which of a set of possible send or receive operations will proceed. It looks similar to a "switch" statement but with the cases all referring to communication operations.

making it overlap with switch makes everything much more confusing.

@chad-bekmezian-snap
Copy link
Author

sekect is currently defined as:

A "select" statement chooses which of a set of possible send or receive operations will proceed. It looks similar to a "switch" statement but with the cases all referring to communication operations.

making it overlap with switch makes everything much more confusing.

It doesn’t overlap with a switch though. It has special behavior specific to errors only that you can’t get with a switch. I agree in some ways it may not be ideal, but it IS backwards compatible and once you’ve learned it I don’t think it is confusing at all.

@earthboundkid
Copy link
Contributor

A simpler proposal is to define ? as != nil and then use switch.

func handler(w http.ResponseWriter, r *http.Request) {
   err := validateHeaders(r.Header)
   var badRequestErr InvalidFieldsErr
   switch {
   case errors.Is(err, ErrUnprocessableEntity):
      w.WriteHeader(http.StatusUnprocessableEntity)
      return
   case errors.As(err, &badRequestErr):
      slog.Error("invalid headers", "fields", badRequestErr.Fields())
      w.WriteHeader(http.StatusBadRequest)
      return
   case err?:
      w.WriteHeader(http.StatusInternalServerError)
      return
   }

  // handle happy path
}

@chad-bekmezian-snap
Copy link
Author

chad-bekmezian-snap commented May 11, 2024

A simpler proposal is to define ? as != nil and then use switch.

func handler(w http.ResponseWriter, r *http.Request) {
   err := validateHeaders(r.Header)
   var badRequestErr InvalidFieldsErr
   switch {
   case errors.Is(err, ErrUnprocessableEntity):
      w.WriteHeader(http.StatusUnprocessableEntity)
      return
   case errors.As(err, &badRequestErr):
      slog.Error("invalid headers", "fields", badRequestErr.Fields())
      w.WriteHeader(http.StatusBadRequest)
      return
   case err?:
      w.WriteHeader(http.StatusInternalServerError)
      return
   }

  // handle happy path
}

That covers about a quarter of the feature set that my proposal does

@ianlancetaylor
Copy link
Contributor

Additionally, I propose a special "abbreviated" select syntax for errors that looks like this select err return "template string: %w", which behaves the same as select err { default: return fmt.Errorf("detailed message: %w", err) }.

This effectively makes the fmt package part of the language. Or, we have to carefully restrict the set of formats that can appear, which is confusing.

@chad-bekmezian-snap
Copy link
Author

Additionally, I propose a special "abbreviated" select syntax for errors that looks like this select err return "template string: %w", which behaves the same as select err { default: return fmt.Errorf("detailed message: %w", err) }.

This effectively makes the fmt package part of the language. Or, we have to carefully restrict the set of formats that can appear, which is confusing.

Yes. Agreed we should be cautious doing that. I also had the thought that we could add a new language feature available only within the context of an error select where, similar to the template packages, a dot represents the current “selected” error value

@thediveo
Copy link

Additionally, I propose a special "abbreviated" select syntax for errors that looks like this select err return "template string: %w", which behaves the same as select err { default: return fmt.Errorf("detailed message: %w", err) }.

This effectively makes the fmt package part of the language. Or, we have to carefully restrict the set of formats that can appear, which is confusing.

Yes. Agreed we should be cautious doing that. I also had the thought that we could add a new language feature available only within the context of an error select where, similar to the template packages, a dot represents the current “selected” error value

A considerable part of my Errorf's add more context information than just wrapping an error, so this would end up in the rabbithole ianlancetaylor points out. Add to that the existing code doesn't seem to get boosted, as visible in your examples.

A generic error.As (ignore for the moment the name's taken) would be almost as good, if I'm not mistaken.

@chad-bekmezian-snap
Copy link
Author

Additionally, I propose a special "abbreviated" select syntax for errors that looks like this select err return "template string: %w", which behaves the same as select err { default: return fmt.Errorf("detailed message: %w", err) }.

This effectively makes the fmt package part of the language. Or, we have to carefully restrict the set of formats that can appear, which is confusing.

Yes. Agreed we should be cautious doing that. I also had the thought that we could add a new language feature available only within the context of an error select where, similar to the template packages, a dot represents the current “selected” error value

A considerable part of my Errorf's add more context information than just wrapping an error, so this would end up in the rabbithole ianlancetaylor points out. Add to that the existing code doesn't seem to get boosted, as visible in your examples.

Can you give me a concrete example of what additional context it is you're referring to?

@thediveo
Copy link

A considerable part of my Errorf's add more context information than just wrapping an error, so this would end up in the rabbithole ianlancetaylor points out. Add to that the existing code doesn't seem to get boosted, as visible in your examples.

Can you give me a concrete example of what additional context it is you're referring to?

No idea what you mean by additional context, it's all in this issue.

@chad-bekmezian-snap
Copy link
Author

A considerable part of my Errorf's add more context information than just wrapping an error, so this would end up in the rabbithole ianlancetaylor points out. Add to that the existing code doesn't seem to get boosted, as visible in your examples.

Can you give me a concrete example of what additional context it is you're referring to?

No idea what you mean by additional context, it's all in this issue.

You said

A considerable part of my Errorf's add more context information than just wrapping an error

Are you saying you typically interpolate additional values? Or what do you mean by “add more context information”?

@thediveo
Copy link

Interpolation.

@susugagalala
Copy link

It is high time the Go team incorporates a "better" error/exception handling framework in Go. All the recent developer surveys atest to this dire need. I salute all the people who came up with proposals to this end, even if most if not all of them got rejected or even rebutted. How long more must Go programmers wait? Until the "IDEAL" error-handling framework comes up? My take is that rather than wait for the IDEAL, settle for one which is "not bad". It was the same with generics or parametric polymorphism. How long did it take for Ian, Griesmer, et. al. to finally come up an acceptable generics implementation in Go? Even Griesmer himself has said the current generics implementation does not entirely fit his ideal, but Go programmers everywhere have already embraced it! So, please! Don't wait for the "ideal" error handling framework for Go to appear. It won't!

@gophun
Copy link

gophun commented May 14, 2024

@susugagalala The Go team already tried it twice. The second proposal, which was an improvement on the first proposal based on feedback, was the most antagonized proposal in the history of Go proposals. You can find it by sorting the closed issues by most downvotes: #32437 By contrast, you can find the community's "counter proposal" (to "leave 'if err != nil' alone") by sorting the closed issues by most upvotes: #32825 So you can't blame the Go team for not wanting to repeat this experience. It's the Go community who really loves and defends the status quo.

@chad-bekmezian-snap
Copy link
Author

It is high time the Go team incorporates a "better" error/exception handling framework in Go. All the recent developer surveys atest to this dire need. I salute all the people who came up with proposals to this end, even if most if not all of them got rejected or even rebutted. How long more must Go programmers wait? Until the "IDEAL" error-handling framework comes up? My take is that rather than wait for the IDEAL, settle for one which is "not bad". It was the same with generics or parametric polymorphism. How long did it take for Ian, Griesmer, et. al. to finally come up an acceptable generics implementation in Go? Even Griesmer himself has said the current generics implementation does not entirely fit his ideal, but Go programmers everywhere have already embraced it! So, please! Don't wait for the "ideal" error handling framework for Go to appear. It won't!

Appreciate the passion. Can you share your thoughts on the proposal itself though?

@apparentlymart
Copy link

apparentlymart commented May 14, 2024

This proposal is pretty interesting to me. I like that it focuses not on hiding the error handling but instead on establishing more explicit structure around it.

My main reservation was already stated: using the select keyword for this seems confusing, since this use of it is entirely unrelated to its existing meaning.

It also uses a different syntax for declaration and assignment than the current select does. For example, the badRequestErr as InvalidFieldsErr declares and assigns badRequestErr but does so without using the := operator, which is admittedly only a small inconsistency but an inconsistency nonetheless.

The latter is only a small concern. The first is more concerning to me, as this situation does feel closer to being a switch than it is to a select. I wonder if we could find a variation of it that does use the switch keyword, but does so in a way that's unambiguous with its two current uses.

Type switches already overload the switch keyword with a variation that has slightly different syntax, and so it would be consistent to overload it with one more meaning "error switch". I expect that an "error switch" would be syntactically similar to a type switch, since they are solving similar problems. I don't yet have a concrete idea for what to propose for that, though. (I will think about it some more.)

EDIT: I shared a switch-based variation of this proposal, which also made it non-error-specific, over in #67372. I made it a separate proposal to avoid derailing this one, but since they both have similar motivations they also have a lot of overlap in what they are proposing.


Putting specific syntax concerns aside, I want to state some conclusions I made from the proposal to confirm whether I understood it correctly, and perhaps to prompt further discussion if I didn't.

In the case Identifier: situation, it seems that Identifier is just a normal value expression, expected to return a value of type error, which can then be passed as the second argument to errors.Is. This situation seems relatively straightforward, if so.

In the case a as B: situation, things seem a little more subtle. To say more about that, first here's my attempt at desugaring your "After" example, by which I mean that I think your example would be interpreted as equivalent to the following code:

    err := validateHeaders(r.Header)
    if err != nil {
        if errors.Is(err, ErrUnprocessableEntity) {
          // (this is the body of the first case, verbatim)
          w.WriteHeader(http.StatusUnprocessableEntity)
          return
        }
        {
            badRequestErrPtr := new(InvalidFieldsErr)
            var _ error = *badRequestErrPtr // (in other words: compile-time error if referent doesn't implement error)
            if errors.As(err, badRequestErrPtr) {
                badRequestErr := *badRequestErrPtr
                {
                    // (this is the body of the second case, verbatim)
                    slog.Error("invalid headers", "fields", badRequestErr.Fields())
                    w.WriteHeader(http.StatusBadRequest)
                    return
                }
            }
        }
        {
          // (this is the body of the default case, verbatim)
          w.WriteHeader(http.StatusInternalServerError)
          return
        }
    }

So:

  • InvalidFieldsErr must be something that would be valid to use as an argument to new, producing a pointer to that type.
  • InvalidFieldsErr must be a type that implements error.
  • badRequestErr must be an identifier that would be valid on the left side of a := *v operation, declaring and assigning a new variable.

Does that seem like a correct interpretation of the proposal?


Assuming I understood it correctly, I do like that it mainly just promotes existing error handling patterns to be a language feature that's harder to use incorrectly, rather than trying to hide the error handling altogether.

I expect it would encourage broader use of the errors.Is and errors.As functions (albeit by hiding that they are being called) and thus more use of machine-recognizable error types/values rather than just human-readable error strings. It does seem to mean either incorporating those parts of package errors into the language spec, or having the runtime include private functions equivalent to those which the compiler could generate calls to.

I feel broadly in favor of this, although I do think that overloading the meaning of select isn't ideal and hope we could find a variation that more closely resembles a type switch, using the switch keyword in a new third way.

@chad-bekmezian-snap
Copy link
Author

This proposal is pretty interesting to me. I like that it focuses not on hiding the error handling but instead on establishing more explicit structure around it.

My main reservation was already stated: using the select keyword for this seems confusing, since this use of it is entirely unrelated to its existing meaning.

It also uses a different syntax for declaration and assignment than the current select does. For example, the badRequestErr as InvalidFieldsErr declares and assigns badRequestErr but does so without using the := operator, which is admittedly only a small inconsistency but an inconsistency nonetheless.

The latter is only a small concern. The first is more concerning to me, as this situation does feel closer to being a switch than it is to a select. I wonder if we could find a variation of it that does use the switch keyword, but does so in a way that's unambiguous with its two current uses.

Type switches already overload the switch keyword with a variation that has slightly different syntax, and so it would be consistent to overload it with one more meaning "error switch". I expect that an "error switch" would be syntactically similar to a type switch, since they are solving similar problems. I don't yet have a concrete idea for what to propose for that, though. (I will think about it some more.)

EDIT: I shared a switch-based variation of this proposal, which also made it non-error-specific, over in #67372. I made it a separate proposal to avoid derailing this one, but since they both have similar motivations they also have a lot of overlap in what they are proposing.

Putting specific syntax concerns aside, I want to state some conclusions I made from the proposal to confirm whether I understood it correctly, and perhaps to prompt further discussion if I didn't.

In the case Identifier: situation, it seems that Identifier is just a normal value expression, expected to return a value of type error, which can then be passed as the second argument to errors.Is. This situation seems relatively straightforward, if so.

In the case a as B: situation, things seem a little more subtle. To say more about that, first here's my attempt at desugaring your "After" example, by which I mean that I think your example would be interpreted as equivalent to the following code:

    err := validateHeaders(r.Header)
    if err != nil {
        if errors.Is(err, ErrUnprocessableEntity) {
          // (this is the body of the first case, verbatim)
          w.WriteHeader(http.StatusUnprocessableEntity)
          return
        }
        {
            badRequestErrPtr := new(InvalidFieldsErr)
            var _ error = *badRequestErrPtr // (in other words: compile-time error if referent doesn't implement error)
            if errors.As(err, badRequestErrPtr) {
                badRequestErr := *badRequestErrPtr
                {
                    // (this is the body of the second case, verbatim)
                    slog.Error("invalid headers", "fields", badRequestErr.Fields())
                    w.WriteHeader(http.StatusBadRequest)
                    return
                }
            }
        }
        {
          // (this is the body of the default case, verbatim)
          w.WriteHeader(http.StatusInternalServerError)
          return
        }
    }

So:

  • InvalidFieldsErr must be something that would be valid to use as an argument to new, producing a pointer to that type.
  • InvalidFieldsErr must be a type that implements error.
  • badRequestErr must be an identifier that would be valid on the left side of a := *v operation, declaring and assigning a new variable.

Does that seem like a correct interpretation of the proposal?

Assuming I understood it correctly, I do like that it mainly just promotes existing error handling patterns to be a language feature that's harder to use incorrectly, rather than trying to hide the error handling altogether.

I expect it would encourage broader use of the errors.Is and errors.As functions (albeit by hiding that they are being called) and thus more use of machine-recognizable error types/values rather than just human-readable error strings. It does seem to mean either incorporating those parts of package errors into the language spec, or having the runtime include private functions equivalent to those which the compiler could generate calls to.

I feel broadly in favor of this, although I do think that overloading the meaning of select isn't ideal and hope we could find a variation that more closely resembles a type switch, using the switch keyword in a new third way.

your desugared example is accurate, as are all of your observations

@ianlancetaylor
Copy link
Contributor

This introduces new syntax based on reuse of an existing keyword. It makes certain kinds of error handling cases slightly simpler. However, it doesn't address the biggest complaint that people have with error handling, which is the repetitive use of code like

    if err != nil {
        return err
    }

It's not all that common for people to check whether an error satisfies a list of conditions. When that is needed, we already have syntax for it. The syntax here is slightly shorter, but overall this doesn't seem to be a significant enough improvement to justify the language change.

Therefore, this is a likely decline. Leaving open for four weeks for final comments.

@chad-bekmezian-snap
Copy link
Author

This introduces new syntax based on reuse of an existing keyword. It makes certain kinds of error handling cases slightly simpler. However, it doesn't address the biggest complaint that people have with error handling, which is the repetitive use of code like

    if err != nil {
        return err
    }

It's not all that common for people to check whether an error satisfies a list of conditions. When that is needed, we already have syntax for it. The syntax here is slightly shorter, but overall this doesn't seem to be a significant enough improvement to justify the language change.

Therefore, this is a likely decline. Leaving open for four weeks for final comments.

Understood. It seems highly unlikely to me that we will ever get rid of explicit error checking for every error returned. To do so would almost inevitably lead to some kind of obscure syntax or new control flow— both of which have been reasons for many error handling proposals being declined. That being the case, it feels that the remaining options are those that leave error handling and explicit as is today, but provide marginal gains in simplicity or brevity. That is the goal of this proposal: provide a couple of small enhancements to error handling that hopefully, when added together, can alleviate distracting/noisy code from meaningful error handling

@ianlancetaylor ianlancetaylor changed the title proposal: Go 2: handle errors with select proposal: spec: handle errors with select Aug 6, 2024
@ianlancetaylor ianlancetaylor added LanguageChangeReview Discussed by language change review committee and removed v2 An incompatible library change labels Aug 6, 2024
@findleyr
Copy link
Contributor

findleyr commented Aug 7, 2024

No change in consensus, so declined.
— rfindley for the language proposal review group

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
error-handling Language & library change proposals that are about error handling. LanguageChange Suggested changes to the Go language LanguageChangeReview Discussed by language change review committee Proposal Proposal-FinalCommentPeriod
Projects
None yet
Development

No branches or pull requests

10 participants