-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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: A built-in Go error check function, "try" #32437
Comments
I agree this is the best way forward: fixing the most common issue with a simple design. I don't want to bikeshed (feel free to postpone this conversation), but Rust went there and eventually settled with the The gophercon proposal cites
rather than:
but now we would have:
which I think it's clearly the worse of the three, as it's not even obvious anymore which is the main function being called. So the gist of my comment is that all three reasons cited in the gophercon proposal for not using |
To clarify: Does
return (0, "x") or (7, "x")? I'd assume the latter. Does the error return have to be named in the case where there's no decoration or handling (like in an internal helper function)? I'd assume not. |
Your example returns The error result parameter does not need to be named in order to use |
Edit: Well, I managed to completely forget that panic exists and isn't a keyword. |
The detailed proposal is now here (pending formatting improvements, to come shortly) and will hopefully answer a lot of questions. |
@dominikh The detailed proposal discusses this at length, but please note that |
One clarification / suggestion for improvement:
Could this instead say On first reading, |
I think this is just sugar, and a small number of vocal opponents teased golang about the repeated use of typing |
Not sure why anyone ever would write a function like this but what would be the envisioned output for
If |
I retract my previous concerns about control flow and I no longer suggest using I disagree with the necessity for simplified error handling, but I'm sure that is a losing battle. |
@webermaster Only the last |
Like @dominikh, I also disagree with the necessity of simplified error handling. It moves vertical complexity into horizontal complexity which is rarely a good idea. If I absolutely had to choose between simplifying error handling proposals, though, this would be my preferred proposal. |
It would be helpful if this could be accompanied (at some stage of accepted-ness) by a tool to transform Go code to use
|
I just would like to express that I think a bare I feel I can work with |
The thing I'm most concerned about is the need to have named return values just so that the defer statement is happy. I think the overall error handling issue that the community complains about is a combination of the boilerplate of
func sample() (string, error) {
var err error
defer fmt.HandleErrorf(&err, "whatever")
s := try(f())
return s, nil
}
func sample() (string, error) {
s, err := f()
try(wrapf(err, "whatever"))
return s, nil
}
func wrapf(err error, format string, ...v interface{}) error {
if err != nil {
// err = wrapped error
}
return err
} If either work, I can deal with it. |
This will not work. The defer will update the local
That should work. It will call wrapf even on a nil error, though.
No one is going to make you use |
Why would you return more than one error from a function? If you are returning more than one error from function, perhaps function should be split into two separate ones in the first place, each returning just one error. Could you elaborate with an example? |
@cespare: It should be possible for somebody to write a @lestrrat: Agreed that one will have to learn that @Goodwine: As @randall77 already pointed out, your first suggestion won't work. One option we have thought about (but not discussed in the doc) is the possibility of having some predeclared variable that denotes the |
I actually really like this proposal. However, I do have one criticism. The exit point of functions in Go have always been marked by a Making an exit point of a function that isn't a
This code may look like a big mess, and was meant to by the error handling draft, but let's compare it to the same thing with
You may look at this at first glance and think it looks better, because there is a lot less repeated code. However, it was very easy to spot all of the spots that the function returned in the first example. They were all indented and started with Returning from a function has seemed to have been a "sacred" thing to do, which is why I personally think that all exit points of a function should be marked by |
Someone has already implemented this 5 years ago. If you are interested, you can https://news.ycombinator.com/item?id=20101417
|
I appreciate the effort that went into this. I think it's the most go-ey solution I've seen so far. But I think it introduces a bunch of work when debugging. Unwrapping try and adding an if block every time I debug and rewrapping it when I'm done is tedious. And I also have some cringe about the magical err variable that I need to consider. I've never been bothered by the explicit error checking so perhaps I'm the wrong person to ask. It always struck me as "ready to debug". |
@griesemer Like.. a beginner wouldn't know this, if they have a bug because of this they won't go "of course, I need a named return", they would get stressed out because it should work and it doesn't. var err error
defer fmt.HandleErrorf(err);
Or... Don't suggest using defer like this, try another way that's safer but still readable. |
@deanveloper It is true that this proposal (and for that matter, any proposal trying to attempt the same thing) will remove explicitly visible We are used to immediately recognize |
I have two concerns:
In my experience, adding context to errors immediately after each call site is critical to having code that can be easily debugged. And named returns have caused confusion for nearly every Go developer I know at some point. A more minor, stylistic concern is that it's unfortunate how many lines of code will now be wrapped in I think these concerns would be addressed with a tweak:
This would retain many of the advantages of
|
@s4n-gt Thanks for this link. I was not aware of it. |
@Goodwine Point taken. The reason for not providing more direct error handling support is discussed in the design doc in detail. It is also a fact that over the course of a year or so (since the draft designs published at last year's Gophercon) no satisfying solution for explicit error handling has come up. Which is why this proposal leaves this out on purpose (and instead suggests to use a |
The proposal mentions changing package testing to allow tests and benchmarks to return an error. Though it wouldn’t be “a modest library change”, we could consider accepting func main() {
if err := newmain(); err != nil {
println(err.Error())
os.Exit(1)
}
} |
This comment has been minimized.
This comment has been minimized.
#52175 proposes a variation of the |
我只是一个普通的 go 开发者,并且我也没有做很多底层的开发,主要用 go 来做web开发。从使用感觉上来讲,go 的错误处理机制真的很难用。真的很难理解为什么不用try catch?实现一套异常错误处理机制,就像大部分的语言一样。 这样用不是更好吗?(我自己使用 recover 机制实现了一个try catch)
response := NewJsonResponse(w, r)
Try(func() {
...
方法可能抛出各种异常
...
}).Catch(exception.NotLoggedIn{}, func(err error) {
response.NeedLogin()
}).Catch(exception.LoginExpired{}, func(err error) {
response.LoginExpire()
}).Catch(exception.NoPermission{}, func(err error) {
response.PermissionDenied()
}).Catch(exception.RedisBroken{}, func(err error) {
response.RedisBroken()
}).Catch(&httpErrs.HTTPError{}, func(err error) {
lego.Error("系统可能正在受到 CC 攻击, 请求来源 ", r.RemoteAddr)
response.RequestSpeedTooFast()
}).CatchAll(func(err error) {
response.SysError()
}).Finally(func() {})
一点猜想try catch 大概率不会被官方支持了,golang 的错误处理机制,积重难返。如果使用 try catch 这样的机制,golang的官方包,三方包统统都要改造~ |
Although the discussion on this issue is concluded, I'd like to introduce my library, which offers a solution to the problem we've been addressing through try proposal. One notable question from the try design document is:
My library leverages generics to replicate try's functionality, circumventing these limitations. It introduces distinct functions for handling functions with different return counts. For instance, Check1 is designed for functions with a single return value, while Check2 caters to those with two. This approach simplify function calls, exemplified by: x := e.Check1(strconv.Atoi(a)) I know this issue is closed but I think it wort to share my library here that addressing the issue that we trying to solve with try or catch One of the question in the try design document is as follow:
I implemented a library that using generics to achieve the same functionality of the try. The difference in here for different return type I created a separate function.e.g Check1,work with function have 1 return, Check2 work with function have two return. In this way we can simplify calling functions x := e.Check1(strconv.Atoi(a)) To handle errors, the Check function triggers a panic. Errors can be caught using a defer statement alongside named returns in HandleReturn function to capture and assign the error. Check function "panic" the error. Now we need to catch the error via defer statement. A complete usage example is as follows: func printSum(a, b string) (err error) {
defer e.HandleReturn(func (e error){
err = e
}
x := e.Check1(strconv.Atoi(a))
y := e.Check1(strconv.Atoi(a))
fmt.Println("result:", x + y)
return nil
} Additionally, custom error handlers can be used with the Check function to tailor error handling or add context to errors. Custom error handler can be used as follow: func printSum(a, b string) (err error) {
defer e.HandleReturn(func (e error){
err = e
}
convertionError:= func (err error) error {
return fmt.Errorf("failed to convert to int: %w", err)
}
x := e.Check1W(strconv.Atoi(a)).With(convertionError)
y := e.Check1W(strconv.Atoi(a)).With(convertionError)
fmt.Println("result:", x + y)
return nil
} For more detailed examples and to explore the library further, please visit: https://github.com/mfatihercik/errless. |
If it is a closed issue, why can it still be commented? There are a lot of comments over the years |
People can still discuss the idea even though we aren't going to adopt it. |
Proposal: A built-in Go error check function,
try
This proposal has been closed. Thanks, everybody, for your input.
Before commenting, please read the detailed design doc and see the discussion summary as of June 6, the summary as of June 10, and most importantly the advice on staying focussed. Your question or suggestion may have already been answered or made. Thanks.
We propose a new built-in function called
try
, designed specifically to eliminate the boilerplateif
statements typically associated with error handling in Go. No other language changes are suggested. We advocate using the existingdefer
statement and standard library functions to help with augmenting or wrapping of errors. This minimal approach addresses most common scenarios while adding very little complexity to the language. Thetry
built-in is easy to explain, straightforward to implement, orthogonal to other language constructs, and fully backward-compatible. It also leaves open a path to extending the mechanism, should we wish to do so in the future.[The text below has been edited to reflect the design doc more accurately.]
The
try
built-in function takes a single expression as argument. The expression must evaluate to n+1 values (where n may be zero) where the last value must be of typeerror
. It returns the first n values (if any) if the (final) error argument is nil, otherwise it returns from the enclosing function with that error. For instance, code such ascan be simplified to
try
can only be used in a function which itself returns anerror
result, and that result must be the last result parameter of the enclosing function.This proposal reduces the original draft design presented at last year's GopherCon to its essence. If error augmentation or wrapping is desired there are two approaches: Stick with the tried-and-true
if
statement, or, alternatively, “declare” an error handler with adefer
statement:Here,
err
is the name of the error result of the enclosing function. In practice, suitable helper functions will reduce the declaration of an error handler to a one-liner. For instance(where
fmt.HandleErrorf
decorates*err
) reads well and can be implemented without the need for new language features.The main drawback of this approach is that the error result parameter needs to be named, possibly leading to less pretty APIs. Ultimately this is a matter of style, and we believe we will adapt to expecting the new style, much as we adapted to not having semicolons.
In summary,
try
may seem unusual at first, but it is simply syntactic sugar tailor-made for one specific task, error handling with less boilerplate, and to handle that task well enough. As such it fits nicely into the philosophy of Go.try
is not designed to address all error handling situations; it is designed to handle the most common case well, to keep the design simple and clear.Credits
This proposal is strongly influenced by the feedback we have received so far. Specifically, it borrows ideas from:
Detailed design doc
https://github.com/golang/proposal/blob/master/design/32437-try-builtin.md
tryhard
tool for exploring impact oftry
https://github.com/griesemer/tryhard
The text was updated successfully, but these errors were encountered: