Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.
Sign upProposal: A built-in Go error check function, "try" #32437
Comments
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
|
Your example returns The error result parameter does not need to be named in order to use |
This comment has been minimized.
This comment has been minimized.
|
Edit: Well, I managed to completely forget that panic exists and isn't a keyword. |
This comment has been minimized.
This comment has been minimized.
|
The detailed proposal is now here (pending formatting improvements, to come shortly) and will hopefully answer a lot of questions. |
This comment has been minimized.
This comment has been minimized.
|
@dominikh The detailed proposal discusses this at length, but please note that |
This comment has been minimized.
This comment has been minimized.
|
One clarification / suggestion for improvement:
Could this instead say On first reading, |
This comment has been minimized.
This comment has been minimized.
|
I think this is just sugar, and a small number of vocal opponents teased golang about the repeated use of typing |
This comment has been minimized.
This comment has been minimized.
|
Not sure why anyone ever would write a function like this but what would be the envisioned output for
If |
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
|
@webermaster Only the last |
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
|
It would be helpful if this could be accompanied (at some stage of accepted-ness) by a tool to transform Go code to use
|
This comment has been minimized.
This comment has been minimized.
|
I just would like to express that I think a bare I feel I can work with |
This comment has been minimized.
This comment has been minimized.
|
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 comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
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? |
This comment has been minimized.
This comment has been minimized.
|
@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 |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
|
Someone has already implemented this 5 years ago. If you are interested, you can https://news.ycombinator.com/item?id=20101417
|
This comment has been minimized.
This comment has been minimized.
|
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". |
This comment has been minimized.
This comment has been minimized.
|
@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. |
This comment has been minimized.
This comment has been minimized.
|
@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 |
This comment has been minimized.
This comment has been minimized.
|
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
|
This comment has been minimized.
This comment has been minimized.
|
@s4n-gt Thanks for this link. I was not aware of it. |
This comment has been minimized.
This comment has been minimized.
|
@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 |
This comment has been minimized.
This comment has been minimized.
|
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.
The only thing I (and a number of others) wanted to make |
This comment has been minimized.
This comment has been minimized.
|
Oh no. |
This comment has been minimized.
This comment has been minimized.
|
I see. Go want to make something different from other languages. |
This comment has been minimized.
This comment has been minimized.
|
Maybe someone should lock this issue? The discussion is probably better suited elsewhere. |
This comment has been minimized.
This comment has been minimized.
|
This issue is already so long that locking it seems pointless. Everyone, please be aware that this issue is closed, and the comments you make here will almost certainly be ignored forever. If that is OK with you, comment away. |
This comment has been minimized.
This comment has been minimized.
|
In case someone hate the try word which let them think of the Java, C* language, I advice not to use 'try' but other words like 'help' or 'must' or 'checkError'.. (ignore me) |
This comment has been minimized.
This comment has been minimized.
There will always be overlapping keywords and concepts which have small or large semantic differences in languages which are reasonably near each other (like C-family languages). A language feature should not cause confusion inside the language itself, differences between languages will always happen. |
This comment has been minimized.
This comment has been minimized.
|
bad. this is anti pattern, disrespect author of that proposal |
This comment has been minimized.
This comment has been minimized.
|
@alersenkevich Please be polite. Please see https://golang.org/conduct. |
This comment has been minimized.
This comment has been minimized.
|
I think I'm glad about the decision to not go further with this. To me this felt like a quick hack to solve a small issue regarding if err != nil being on multiple lines. We don't want to bloat Go with minor keywords to solve minor things like this do we? This is why the proposal with hygienic macros #32620 feels better. It tries to be a more generic solution to open up more flexibility with more things. Syntax and usage discussion ongoing there, so don't just think if it being C/C++ macros. The point there is to discuss a better way to do macros. With it, you could implement your own try. |
This comment has been minimized.
This comment has been minimized.
|
I would love feedback on a similar proposal that addresses a problem with current error handling #33161. |
This comment has been minimized.
This comment has been minimized.
|
Honestly this should be reopened, out of all the err handling proposals, this is the most sane one. |
This comment has been minimized.
This comment has been minimized.
|
@OneOfOne respectfully, I disagree that this should be reopened. This thread has established that there are real limitations with the syntax. Perhaps you are right that this is the most "sane" proposal: but I believe that the status quo is more sane still. I agree that I know a lot of developers lament the "longhand" error checking in go, but honestly terseness is often at odds with readability. Go has many established patterns here and elsewhere that encourage a particular way of doing things, and, in my experience, the result is reliable code that ages well. This is critical: real-world code has to be read and understood many times throughout its lifetime, but is only ever written once. Cognitive overhead is a real cost, even for experienced developers. |
This comment has been minimized.
This comment has been minimized.
|
Instead of: f := try(os.Open(filename))I'd expect: f := try os.Open(filename) |
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
|
It would be nice If we could use try for a block of codes alongside with the current way of handling errors. // Generic Error Handler
handler := func(err error) error {
return fmt.Errorf("We encounter an error: %v", err)
}
a := "not Integer"
b := "not Integer"
try(handler){
f := os.Open(filename)
x := strconv.Atoi(a)
y, err := strconv.Atoi(b) // <------ If you want a specific error handler
if err != nil {
panic("We cannot covert b to int")
}
}The code above seems cleaner than the initial comment. I wish I could purpose this. I made a new proposal #35179 |
This comment has been minimized.
This comment has been minimized.
|
val := try f() (err){ |
This comment has been minimized.
This comment has been minimized.
|
I hope so: i, err := strconv.Atoi("1")
if err {
println("ERROR")
} else {
println(i)
}or i, err := strconv.Atoi("1")
if err {
io.EOF:
println("EOF")
io.ErrShortWrite:
println("ErrShortWrite")
} else {
println(i)
} |
This comment has been minimized.
This comment has been minimized.
|
@myroid I wouldn't mind having your second example made a little bit more generic in a form of i, err := strconv.Atoi("1")
switch err != nil; err {
case io.EOF:
println("EOF")
case io.ErrShortWrite:
println("ErrShortWrite")
} else {
println(i)
} |
This comment has been minimized.
This comment has been minimized.
|
@piotrkowalczuk Your code looks much better than mine. I think the code can be more concise. i, err := strconv.Atoi("1")
switch err {
case io.EOF:
println("EOF")
case io.ErrShortWrite:
println("ErrShortWrite")
} else {
println(i)
} |
This comment has been minimized.
This comment has been minimized.
|
This does not consider the option there will be an eye of different type
There needs to be
Case err!=nil
For errors the developer did not explicitly capture
…On Fri, 8 Nov 2019, 12:06 Yang Fan, ***@***.***> wrote:
@piotrkowalczuk <https://github.com/piotrkowalczuk> Your code looks much
better than mine. I think the code can be more concise.
i, err := strconv.Atoi("1")switch err {case io.EOF:
println("EOF")case io.ErrShortWrite:
println("ErrShortWrite")
} else {
println(i)
}
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#32437?email_source=notifications&email_token=ABNEY4VH4KS2OX4M5BVH673QSU24DA5CNFSM4HTGCZ72YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEDPTTYY#issuecomment-551500259>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABNEY4W4XIIHGUGIW2KXRPTQSU24DANCNFSM4HTGCZ7Q>
.
|
This comment has been minimized.
This comment has been minimized.
|
|
Proposal: A built-in Go error check function,
tryThis 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 boilerplateifstatements typically associated with error handling in Go. No other language changes are suggested. We advocate using the existingdeferstatement 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. Thetrybuilt-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
trybuilt-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
trycan only be used in a function which itself returns anerrorresult, 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
ifstatement, or, alternatively, “declare” an error handler with adeferstatement:Here,
erris 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.HandleErrorfdecorates*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,
trymay 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.tryis 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
tryhardtool for exploring impact oftryhttps://github.com/griesemer/tryhard