To make an attempt at articulating why the error handling in Go is valuable as-is, I note the following points:
- It is explicit, not hidden - The default idiom highlights "error checking being done here" (Go developers learn to look for lines "if err != nil".) - It is possible to set breakpoints on the error case - Code coverage of unit tests reports when the error case has been exercised. - It has the full flexibility of identifying the failure scenario, not limited to err != nil (although that is the overwhelming use) - It has the full power and flexibility of the language for handling the error - it can be logged, returned, recovered from, sent to a channel, etc. - If wrapping my error extends to multiple lines (due to longer messages, number of parameters in the error, etc., that works using existing language constructs. - It supports arbitrary return statement complexity, including multiple return values. - It is straightforward to parse / analyze. That said, it has some drawbacks, particularly in the most common cases. - Vertical space: Even in complex cases, the error handling inside the "if" block can be reduced to at most two statements. One line for "handling" the error, and a second line for returning it (or a modified / wrapped version of it). With two additional vertical lines of boilerplate, that's either a 100% or 200% increase in vertical space (the "if" statement, and the closing brace). This limits the amount of code that can fit on my screen, which constrains my ability to comprehend larger functions. - Verbosity: The moment we're know we're checking for errors, the "if err != nil {", the "return", and the "}" are usually just extra syntax. - Scanning complexity: What if the check is not err != nil, but err != NotFound? Is this an error case, or an expected case? The current construct hides that distinction. We have to look to the next line to see how it is handled to know that it is an error return. - When there are multiple returns on a function, the error handling must specify them all, even though the common case is to return a "zero" value. For language design purposes, the most common scenario for handling an error is a single-line error return. When it comes to error handling, there are two paths to choosing a new solution: - Identify a solution that captures the 80-90% case of if err != nil { return ... } - Identify a solution that can be used for *all* error handling, where the existing if err != nil then becomes a legacy code base choice, because it does not use the new construct which more clearly conveys "error handling here". For the benefit of the language, I would prefer a solution that fits the second category, not one that fits the first category. Due to the fact that this proposed solution does not address the arbitrary response complexity, it seems like it fits the first category. If there's a way to tweak the proposal so that it can be used for *every* error handling case, then that would be better. Go has a long history of implementing orthogonal solutions that happen to combine well. It is possible that error handling is one of those places that could benefit from teasing this question apart a little more. Eric On Thursday, February 6, 2020 at 7:28:24 PM UTC-8, addi...@gmail.com wrote: > > Error checking in Go: The try keyword. > > This isn’t a complete proposal it only describes basic idea and the > changes suggested. > The following are modifications that deals with some of the problems > introduced in the original proposal > <https://github.com/golang/proposal/blob/master/design/32437-try-builtin.md> > . > > (I apologize if something very similar that uses a simple method to deal > with adding context has been posted before but I could not find it.) > > First, try is a keyword not a function builtin. > > Here’s how error handling with the try keyword works: > > try err > > is equivalent to: > > if err != nil { > return err > } > > That’s it. > > Example: > > f, err := os.Open("file.dat") > try err > defer f.Close() > > f.WriteString("...") > > But, how to add context to errors? > > because try only returns if err != nil. > You can create a function that returns nil if an error is nil. > > In fact, a function like this already exists in the errors package here. > https://github.com/pkg/errors/blob/master/errors.go > > // Wrap returns an error annotating err with a stack trace > // at the point Wrap is called, and the supplied message. > // If err is nil, Wrap returns nil. > func Wrap(err error, message string) error { > if err == nil { > return nil > } > err = &withMessage{ > cause: err, > msg: message, > } > return &withStack{ > err, > callers(), > } > } > > Example using it with try > > f, err := os.Open("file.dat") > try errors.Wrap(err, "couldn't open file") > defer f.Close() > > f.WriteString("...") > > That’s it. > > This should reduce repetitiveness with error handling in most cases. > > It is very simple to understand, and it feels like Go. > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/f90d43bb-58e0-4cff-9149-48c4082ab183%40googlegroups.com.