-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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: "trap" keyword for func-wise error handling #56258
Comments
I think it's fairly surprising that assigning a value to |
I must say that I am purely an application level developer, with focus on cloud API. i.e., I am biased and am not proficient at performance, low level things or language design e.g. generics. After I submitted this proposal, I read carefully about the "try" proposal, and to be honest, I mostly agree with that proposal. It is a pity that the "try" proposal is rejected. I don't know what are the concerns of the community, for me, the most important language level concern is that it should be I must emphasis again that I am not sensitive about application performance, I trust that to the go team to improve the compiler and architecture of Go ecosystem. However, I found myself write this kind of code again and again:
You can see how many "assert" I have used in this short piece of code. In essence, when writing code you literally have to check errors wherever there could be one. BTW, long time ago I have an informal proposal which is informally rejected, that is adding the Returning to your comment, @ianlancetaylor , I don't think assigning value to One final comment on this: I think certain level of implicit behavior is good for code quality, as stated in my proposal, the most important benefit of my proposal is to catch errors that are ignored by inexperienced programmers, for example, many of them do not have the habit to check errors when calling |
The fact that Being succinct is generally good, but it's not the only goal. In general Go aims to optimize for the person reading the code, because most code is read more often than it is written. We don't optimize for the person writing the code. So all else being equal, writing less code is good. But it's more important that the resulting code be clear and easy to understand for a reader who is not familiar with it. You want to write fewer |
@ianlancetaylor ok, I understand your concern, but I want to emphasize situations that I and my colleagues encounter countless times are, for example:
What is your opinion on how the language design itself should help programmers on making less mistakes, or, relief cognitive overload? As to bias toward reader or writer of the code, I 100% agree that go should be "plain", by avoid using symbols everywhere (although I believe the C ternary operator is loved by many), I do think that help programmer write bug free code is also important, just like the design of Go and my other languages to have automatic memory management. |
@xrfang you should consider using my try library. It implements close to what A nice benefit of the try library is things mentioned/implied as benefits of this proposal:
I like this proposal but unfortuantely it is dead on arrivale because the Go maintainers have decided (based on community feedback) that an early return should be done by something quite obvious like a keyword statement. |
@gregwebs thank you for try library, I will take it a look, as well as the original library yours is forked from. To be honest, what you'v written is exactly same in concept with my assert mechanism. I have not published a library, because I personally think it is a bit heavy to rely on another package to do error handling that is so frequently required. As to annotation, I did something like I don't know what is the criticism on try-catch (as in Java or C++) by the Go designer and the community. But I think the most headache Go's error handling caused me is lack of stack trace, even if you do If anybody is reading this, please kindly point me to any literature about why Go does not like try-catch -- I personally think the |
https://go.dev/doc/faq#exceptions Also note that you can use the runtime library to add the stack trace information to an error, the line it occured at and so on if you want. Go handles errors in a more fine grained way. Try-catch does not allow to reason about domain errors easily: it treats everything as a program error which is too coarse and thus confusing since the handling occurs out of natural program flow. |
@xrfang my try library uses the errors library underneath, which is a fork of the original pkg/errors library whose goal was really just to add stack traces via wrapping. There is proposal work done by the Go team to add stack traces to errors. It is a shame that it hasn't been followed through on. Although the wrapping standard means we can do a decent job solving the issue in the library space. You might put your code out on github, not for others to depend on as a library, but just so that people like myself can look at it. I am very much in agreement with Go on avoiding exceptions, but I think the Go FAQ on exceptions does not actually state the negative effect of exceptions. |
@gregwebs here is my errors.go I didn't release it simply because I think using a library for error handling is too heavy, and that's why I am reluctant to adopt In my errors.go, the I personally do NOT agree with the criticism that try-catch makes flow control a mess, or it should only be used on exceptions that are not handle-able, and should terminate the program. In lots of my applications, especially those doing background data processing in a standalone goroutine, it is absolutely vital to catch all exceptions and handle them nicely, even for things like network outage, I want my program to keep trying, and automatically resume after error condition vanishes. Anyway, throw-try-catch is not meant to be the only way to handle errors, especially in Go context. In fact panic is 100% inevitable, typical non-environmental cause is to use invalid index on a slice. Why the Go people are so afraid of try-catch? :-) @ianlancetaylor |
Thanks, it is interesting for me to see these implementations. It is also interesting to see how different Go programmer place some kind of hard constraint on error handling. In your case the contraint seems that panics should be handled very precisely.
I guess that is why my library is named just
That is interesting, but since it requires a
I am able to do that with the existing error system. Panics make this more difficult for me because the program can now potentially be unwound first before the retry.
This proposal is dead on arrival. Give my |
@gregwebs I do use Here I invite you to take a look at my minimalistic logging package with a |
It is quite useful for example to add a database ID. Then you can lookup the db state that caused the panic and can reproduce more quickly. Strictly speaking not necessary since you can deduce all possible edge cases, but sometimes it makes things go a lot faster.
Yes, it is the same except that I re-throw normal panics after annotating them. Your package returns them as errors. I had a version of Handle that did exactly that, but the problem is that a program using the try library might end up not printing out the stack trace of the error (by using "%v" instead of "%+v"). Whereas if I rethrow the panic the program of the library is still going to crash and print out the stack trace. It is interesting to see different library authors independently coming up with the same creative solutions. |
@gregwebs I'm sorry, I may not understand the community culture very well, this is the first time I participate in the discussion, I take the liberty to ask a question: |
@kiqi007 not that I know of. There are hacks to have macros in Go. There is of course the Go generate tool and tools for analyzing and modifying go source code (look into Go fix and linters). The failpoints tool alters Go code and then reverts it when testing is completed. |
@gregwebs Just if possible, add user entry points for lexical analysis or syntax analysis at compile time. This allows the user to implement the syntactic sugar they expect |
@kiqi007 I guess your idea is not compatible with go culture. As @ianlancetaylor pointed out, Go language (and compiler?) is aiming at optimize for reader, not writer. I have no idea at all about compiler design, however, I think the idea of macro and compile time switches are very useful and some of these already exists in Go. If your idea could be used to add "built-in" functions (which are actually user-defined functions), for example, my |
@gregwebs For example, when the syntax analysis reads: This is similar to using go-linter-tools to change the code before go compile: replace all Therefore,for the user, it reads as if the go language had the try/trap keyword |
@xrfang |
This proposal changes the meaning of Similar ideas have been proposed like the Therefore, this is a likely decline. Leaving open for four weeks for final comments. |
No further comments. |
Author background
I am an experienced Go programmer who programmed in Go for over 10 years. I have also used many other languages such as PHP, JavaScript, ObjectPascal (Delphi), as well as loads of others which I am not so proficient or have not been using them for decades.
Related proposals
This is a proposal about Go's Error Handling. I have seen many proposals on this topic, for example: #56165 (
try
statement), #37141 (pass
statement), most of them focus on simplifying the infamous go boilerplate:This proposal is different in that it is a schema about default error handling, which I think may not only simplify coding, but also improve code quality in the long run.
Proposal
A new keyword
trap
should be added in order to define a "default" error handler for all error-generating statements, inside a func. It could be used in any of the following forms:trap &error
trap func(error)
trap
The essence of this statement is that it automatically catches error, in the following two cases:
f, _ := os.Open(...)
, or implicitly, by not assigning to any variable, i.e.f.Close()
.In the
Func1()
example, trapped error is assigned to theerr
variable, inFunc2()
, passed to the handler, and inFunc3()
panic-up to the caller. It is worth pointing out that the last case, direct panic-up is especially useful, because of the trap facility is designed to automatically check for errors that is ignored by the programmer, e.g. does not check the return of f.Close(), os.Rename(), db.Exec()...Compatibility
This feature is supposed to be 100% compatible with Go1, because it ONLY works in case the programmer does NOT handle error. Consider the following case:
Orthogonality
This feature may be implemented by the compiler using mechanism similar to "defer". It may be used multiple times, and be mixed with "defer".
Further Improvement
Although not mandatory, I think the trapped err should by default include a stacktrace to make it useful, just like errors.WithStack() in the errors package. I propose the following interface:
Another reason that the error should be naturally stack-traced is that the
panic/recover
mechanism accept/returns aninterface{}
, noterror
. The trap mechanism may be implemented as:The above sample shows one difference/incompatibility between defer and trap: trap accept an argument, while defer not.
Alternatively, another keyword might be used:
trace
to indicated that the trapped error should have stack information, like so:Personally, I think trapped error should always include stack information.
Costs
This proposal may not make Go easier or hearder to learn, and the cost is minimal, for the sake of the language specification.
As to the impact of the compiler or the tool chain, I think it indeed may have some work to do, especially on the tool chain, sorry I don't know which tool should be modified to add such feature, but I would expect VS Code (the language server?) should warn the programmer if it sees code such as
f.Close()
without assigningits return value:
The text was updated successfully, but these errors were encountered: