3 ms·
I'd like stuff like myFunc() works as today myFunc()^ returns the err if there is one myFunc()! panics if there is an err
by treis 4y ago
I'd like stuff like
myFunc() works as today
myFunc()^ returns the err if there is one
myFunc()! panics if there is an err
- klodolph 4y agoI don't think any option should panic on error. I've had to deal with programs that panic on error and the way I dealt with it was by forking the project and removing every call to panic(). It's even worse with libraries. The other problem is that it's super common to want to wrap the error somehow when returning it. Often you want to wrap the error differently depending on where the error came from in the function, like this: d, err := os.ReadFile(fname) if err != nil { return err // unwrapped } x, err := parseX(d) if err != nil { return fmt.Errorf("%s: %w", fname) } I know that not all Go developers write good error handling code. However, I don't want to make it easier to write bad error handling code if you're not making it correspondingly easier to write good error handling code.
- metaltyphoon 4y agoUnless you put the function name somewhere in the error, there is no way to know where something happened. "Some thing didn't write properly? Cool! Where and which line?"
- dannymi 4y agoIf it's an internal (unexpected) error, panic. You get a backtrace and everything--which helps because it was an unexpected internal error, so you probably messed up. You'd like to know where that is. But if it's an actual user-visible error code then that means you expected for this error to happen eventually and you designed for it. Why then do you need a backtrace? You program is doing what it's supposed to. I mean sure while developing it's nice to be able to just replace the error code emission by panic (which I do sometimes)--but in regular operation? It doesn't help the user at all.
- klodolph 4y ago> "Some thing didn't write properly? Cool! Where and which line?" My eyes glaze over when I'm trying to read stack traces in Java or Python, and then it turns out I can't figure out what's wrong because the stack trace doesn't include the path of the file that failed to parse. What we care about is stuff like: - Program tried to parse file X, - Failed because file X doesn't exist, or because file contains syntax errors, or something - Some context giving us the reason why program tried to read file X, which is usually at a fairly high level (something like "could not load config"). Putting the function name in the error-handling code is something that you SOMETIMES do, but only when it adds important context. If you do it all the time you end up with something completely unreadable, like a stack trace. There are various libraries you can use that give you stack traces for errors in Go, if that's what you want. If I get an error that X is not a valid JSON file--often, that's all I need to know, because I can open X and see the syntax error and fix it. Knowing the name of the function that detected the syntax error is unhelpful, usually, and we have logging + debuggers to help with those cases.
- lenkite 4y agoJava stack-traces are extraordinarily informative - they give line number, class, method and even offset inside relative lambda when an exception occurs. Few languages do better and Go is not one of them.
- jen20 4y agoAborting is almost always the correct answer in order that you get a core dump that may be debugged post-mortem. Blindly continuing when in an invalid state is a horrible idea.
- majewsky 4y ago> myFunc()! panics if there is an err You can do this with generics today: func must[T](val T, err error) T { if err != nil { panic(err.Error()) } return val } //usage example file := must(os.Create("out.txt")) defer file.Close() bytesWritten := must(file.Write([]byte("Hello World\n"))) We have something like this in our internal utility library. I'm getting some serious mileage out of this when using Go as a script language, where error propagation is not as important as for a regular application or a library. > myFunc()^ returns the err if there is one You can also do this with generics: func geterr[T](val T, err error) { return err } It is true that must() and geterr() are slightly more verbose than single-character operators, but I'd argue having a word instead of a symbol is much more readable. Besides, ^ cannot be used as a postfix unary operator because it's already a binary operator. That would create a grammar ambiguity.