-
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: Go 2 error values #29934
Comments
Assuming this is accepted and implemented before generics, would the design be adapted in a potential redesign of |
@mvdan We think the design not using generics resulted in a slightly nicer API in the end. So we will probably not do so. That said, it can't be ruled out there will be additional API using generics. |
Could a change in output break consumers which compare it with a hard-coded string, e.g. test code? |
Conceivable, but since the |
I wonder why
not
|
Agreed it's odd that the format string is an output-type controller, rather than a flag in the verb like Also, have the authors considered providing this, which is conspicuously absent in package fmt:
This works like |
What are the arguments for a flag over a verb? I don't see a strong argument for one over the other and
I think this is orthogonal to the rest of the proposal, but it doesn't seem unreasonable. On the other hand, I don't think I've ever felt the absence of this function. |
I'm referring to this output-type control via the format string: "if ... the format string ends with The easiest way to print an error is: |
The only existing flags that don't have a meaning for %s and %v are space and 0. But those both feel wrong, so we'd need to make up a new flag. That means one could potentially use that flag with any verb, but it would be meaningless except for %s and %v. That feels wasteful—flags and verbs should combine orthogonally to (almost) always produce a useful result (with space and 0 themselves being the notable exceptions). |
To be clear: does this mean each call to fmt.Errorf will collect a stack trace? |
A single stack frame, not a full trace. |
Would it be possible to make it "adjustable" somehow? In our home-made logging wrapper we have noticed that we have to pull a couple or 3 frames to get to "the caller of interest", so printout looks more or less pointing to the place where We did it by (arguable not so elegant) way as adding a parameter "how big is the stack offset" to the logging function, but I am not sure if that's the only option here. Adding special format symbol for stack, with number of frames interesting might be one of possible approaches. |
The intended usage is that additional frames be added as necessary by annotating or wrapping the error, possibly with additional context. For example, func inner() error { return errors.New("inner error") }
func outer() error { return fmt.Errorf("outer error: %w", inner()) }
fmt.Fprintf("%+v", outer())
// outer error:
// /path/to/file.go:123
// - inner error:
// /path/to/file.go:122 To attach information about where a utility function was called from, you do so in the same way as today: By annotating the error with additional information. We considered capturing a full stack rather than a single frame, but think the single-frame approach has some advantages: It's light-weight enough to be feasible to do for every error and it deals well with error flows that pass between goroutines. That said, this proposal makes it easy for error implementations to add whatever detail information they want in a compatible way, be it full stacks, offset stack frames, or something else. |
Erm, I think you've refuted your own argument by raising exceptions :-)
|
I'm not certain if you're making an case about verbs vs. flags ( |
As I said above, I'm referring to this output-type control in the format string: if ... the format string ends with The control should be a flag e.g. |
Thanks; I think I understand you now. The current design of applying special-case handling in Firstly, we want to permit existing code to take as much advantage of the new error formatting features as possible. So, for example, func f1() error { return errors.New("some error") }
func f2() error { return fmt.Errorf("f2: %v", f1()) }
func main() {
fmt.Printf("%+v", f2())
} We want this to print:
But if annotation is only enabled with a special format directive like
The innermost stack frame is lost. The other consideration is that error annotation is linear. The // What would this error's `FormatError` method do?
return fmt.Errorf("some information about error: %$v (with some more information)", err) These two considerations led to the current design of automatically annotating errors produced by |
Re the stack frame, annotation could be enabled by any use of an
Would a format string |
No; we will only treat an error specially if the format string ends with ": %s" or ": %v".
Can you explain why? Our goal with this feature is to instantly and painlessly bring the new formatting into most existing Go code. We'd rethink that if, for example, it turned out that most existing calls to As Damien pointed out, if you do like to put errors in the middle of your format strings, you're going to have bigger problems with our proposal than its One more point: while we do expect people to continue to write
|
My concern here would be that "Something happened: This is What" is an ok way to produce a short human-readable error, but... Even in English, if I would want some text explaining "What to do now?" (remediation suggestion, "contact support" etc), that Would it be easier to introduce a special formatter "print this as 'error'" and a modifier to make it "print this as an error with a stack"? I think there are enough letters in English alphabet left to cover one more case. Also, if I may ask - no magic, please. |
It would also be nice to have a standard helper that can get to the root cause, i.e. the last error in the chain. Similar to https://godoc.org/github.com/pkg/errors#hdr-Retrieving_the_cause_of_an_error. |
There are two points here, and it's worth considering them independently. The first is the manner in which error annotations compose. The proposal adds a new A primary goal of the Detail formatting of an error (
Displaying a chain of errors in a consistent fashion requires making certain decisions about formatting: Do we print errors from the outermost to innermost, or vice-versa? How do we indicate boundaries between errors? If we leave these formatting decisions up to individual error implementations, we are likely to have situations where, for example, part of an error chain is printed inner-to-outer and another part is outer-to-inner; very confusing and not a good user experience. To avoid confusion and simplify the work required to write a custom error type, we put the formatting of the error chain under the control of the error printer. Errors provide three pieces of information: The error text, detail text, and the next error in the chain. The printer does the rest. So, for example, the outermost error in the above example might have an func (e errType) FormatError(p Printer) (next error) {
p.Print("could not adumbrate elephant") // error text
if p.Detail() {
p.Print("(some additional detail)") // error text only visible in detail (%+v)
p.frame.FormatError(p) // print stack frame as detail
}
return e.wrappedErr // return the next error in the chain
} Note again that an important property of this design is that an error doesn't choose how to format the error that it wraps; it returns that error to the printer, which then determines how to format the error chain. Not only does this simplify the error implementation and ensure consistency, but this property is important for localization of errors in RTL languages (as mentioned by @tandr), because it permits a localizing printer to adjust the output order as appropriate. (Most users will print errors using the Given the above, the question then is how to make it simple for users to produce errors that satisfy these interfaces, which is where we get to the special handling of a suffix |
The equivalent in this proposal is the if errors.Is(err, io.EOF) { ... } We believe this has several nice properties over a function returning the last element in a chain:
|
I like the motivation of the new To be a bit more concrete, I have more than once found myself in a similar situation with Python, where I raise an exception using f-strings but forget to put the "f". raise RuntimeError("oh no! Something happened: {important_information}") ... instead of ... raise RuntimeError(f"oh no! Something happened: {important_information}") And since there's nothing syntactically wrong with what I did, I might not know until later when I need important_information that I've made a mistake. |
Although that seems like a laudable goal, modifying the way existing code creates output is inherently risky. I suspect most of us would rather have an opt-in mechanism. Let project owners trial a feature and switch over (via go fix) when convenient for them. IOW, new error-construction functions are needed, which always return errors.Formatter. |
While the fmt.Errorf changes to wrap the error is neat and conserves backwards compatibility with previous versions, a slightly nicer option going forward might be good? When using pkg/errors, |
Go 1.14 is out and I was curious if any one these made into Go v1.14 but couldn't find anything in the changelog. Can we assume it's now postponed to 1.15? What is the latest status on capturing and including stack frames inside Errors? |
@fatih There are no more plans for error handling. It is delayed at least after generics. You need to wait years. |
https://blog.golang.org/go1.15-proposals
https://golang.org/issue/33162#issuecomment-559183019
|
Thank you @changkun for the links. @jba @rsc based on the links that @changkun provided, can we assume that error handling work is suspended (or paused)? If that is the case we should update the issue with a notice reflecting the latest status. There are many people (including me) assuming this is still and on-going work. I was assuming that we would receive some of the functionality, such as stack frames and was planning accordingly for that internally at GitHub. I need to revisit my planning and inform teams at GitHub that the work on errors is delayed (or suspended) if that is the case. |
@JavierZunzunegui thanks for the link. Is there any additional benefit compared to |
(+) It provides much more formatting flexibility. Skip want you want, encode the others any way you want, if you want to you can even reverse the encoding order. |
About including frame, there are a lot of lib implementing this, and it's very easy to do, i did it myself to. But all of them are unusable in library if there is not at least a common interface to know if an error already embed the frame and how to print it. |
@flibustenet of course, the ideal place for any such solution is the standard library, but that is not happenning any time soon. We are in the same place with frames / formatting that we were with wrapping pre-1.13: multiple, sometimes poorly compatible frameworks. But that is the way to experiment and ultimately show, through experience, what is best, and guide the decision of what makes it to the standard library. That was very much the story of wrapping too. |
The issue with zerrors linked above is that the error objects are not suitable for transporting over the wire in a distributed application. It's not possible to reliably inspect stack traces and frame information across servers. See crdb's own errors replacement which supports network transport and is battle-tested in production. |
the crdb errors package also has its own formatting protocol for |
@changkun We are unlikely to do anything about error handling any time soon. But this proposal is not about error handling. It is about error values. They are different things. See, e.g., the separate discussions of error values and error handling at https://go.googlesource.com/proposal/+/master/design/go2draft.md. |
This comment was marked as outdated.
This comment was marked as outdated.
Shouldn't this issue be closed? The proposal was accepted and shipped with Go 1.13. As @ianlancetaylor pointed out this issue was about the error values proposal, not about error handling in general. This is currently the most commented open issue, so I was expecting something interesting going on here, but it's already done. |
Comparing errors using DeepEqual breaks if frame information is added as proposed in golang/go#29934. Updates golang/go#29934 Change-Id: Ia2eb3f5ae2bdeac322b34e12d8e732174b9bd355 Reviewed-on: https://go-review.googlesource.com/c/164517 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Damien Neil <dneil@google.com>
This issue is for discussion of our Go 2 error values proposal, which is based on our draft designs for error information and formatting.
This proposal will follow the process outlined in the "Go 2, here we come" blog post: we will have everything ready to go and checked in at the start of the Go 1.13 cycle (Feb 1), we will spend the next three months using those features and soliciting feedback based on actual usage, and then at the start of the release freeze (May 1), we will make the "launch decision" about whether to include the work in Go 1.13 or not.
Update 2019-05-06: See launch decision here: #29934 (comment)
The text was updated successfully, but these errors were encountered: