> panic and recover are best reserved for exceptional circumstances.
You might go with Joshua Bloch and say exceptions are also best reserved for exceptional circumstances (which actually only means "things aren't working as expected"), that's why Go's authors used "panic" instead of "throw" or something similar, to make clear that it shouldn't be used where you might use exceptions in other languages. I mean, it's in the FAQ too: https://go.dev/doc/faq#exceptions
aqueueaqueue 2 hours ago [-]
You know why I hate exceptions most?
When debugging be it C# or JS, neither the "break on all exceptions" or "break on caught exceptions" are useful on any app. One just hits random library shit, or whatever bloat is in the codebase all the time and the other won't break at all.
But because exceptions are the control flow that is the only way to debug them (or do a human binary search)
Not sure what go debugging is like but I imagine you can quickly work your way to the first err!=nil while debugging.
mrighele 57 minutes ago [-]
I don't know about C#, but in my Java IDE when I set a breakpoint on an exception I can set a filter not only on the class being throw, but also on the class that catch it, the one that throws it and the caller method, and to trigger only after another breakpoint is hit or it is the nth times it has been passed. With this you can make it trigger only when needed in a farly easy way
CharlieDigital 35 minutes ago [-]
It's the same for the mainstream debuggers for .NET.
rs186 1 hours ago [-]
Well, "break on exceptions" can be very powerful when used correctly, i.e. when the scope is narrowed down. It should never be a flag that is turned on all the time -- that's guaranteed misery there.
> quickly work your way to the first err != nil while debugging
I doubt you'll spend any less time debugging in Go. If you disagree, I'd love to see a "side-by-side" comparison for code that's functionally the same but written in both Go and JS, and see some explanations why it's easier in Go
7bit 43 minutes ago [-]
You can limit those to code you write. But it sounds like you break also on code that you didn't write eg, libraries or modules. Of course you're miserable.
9rx 2 hours ago [-]
> which actually only means "things aren't working as expected"
Exceptional circumstances, or exceptions for short, mean "things aren't working as expected due to programmer error". In other words, a situation that theoretically could have been avoided by a sufficiently advanced compiler but that wasn't caught until runtime.
"things aren't working as expected" is vague enough to include errors, which are decidedly not exceptions. One might say a hard drive crash or the network failing isn't working as expected, but those situations are not exceptional.
> to make clear that it shouldn't be used where you might use exceptions in other languages.
Other languages are starting to learn that you shouldn't use exception handlers where you wouldn't use panic/recover, so I'm not sure there is a practical difference here.
daveliepmann 1 hours ago [-]
>Exceptional circumstances, or exceptions for short, mean "things aren't working as expected due to programmer error".
Interesting. In Javaland this describes assertions, and the term exception is for operating errors, i.e. problems not necessarily attributable to programmer error, including your example of a network failure.
bazoom42 2 hours ago [-]
Terminology is a problem here. A crashed harddisk is clearly an exceptional circumstance. More specific terms is needed to distinguish errors in the code (eg divide by zero) from unpreventable errors like network failure.
tored 17 minutes ago [-]
Isn't that what we have exception hierarchies for?
jjmarr 3 hours ago [-]
I've solved n-queens once before using exceptions to handle control flow. I coded a recursive solution for an assignment, but I wrote it wrong and it ended up printing all of the possible solutions instead of just one.
Because I didn't have much time before the final submission, I just put the initial call in a try catch block and threw an exception to indicate successful completion.
CJefferson 7 minutes ago [-]
Honestly, this is the best way to write recursive algorithms in my opinion (and I write a lot of recursive search algorithms in my research).
The alternative is every single function has to return a boolean, along with whatever else it would return, which is true when you have found a solution, and you then return straight away -- effectively just reimplementing exceptions by hand, which doesn't feel like a useful use of my time.
actionfromafar 1 hours ago [-]
Enterprise ready. :)
shric 3 hours ago [-]
I programmed in Go for 5 years (stopped 2 years ago) and didn't even know the language had recover() until 5 minutes ago.
I used panic() all day, but never recover. I use panic for unrecoverable errors. I thought that's why it's called "panic".
tgv 4 minutes ago [-]
There may be libraries that call panic. E.g., the templating library does that. In that case, I want something in the logs, not a termination of the service.
DanielHB 9 minutes ago [-]
In my old project we used recover to give a HTTP 500 response, trigger a log/alert and restart our HTTP router and its middlewares in case of panic in some function.
Restarting like that was faster and more stable than crashing the whole thing and restarting the whole server. But it is a bit dangerous if you don't properly clean up your memory (luckily most APIs are stateless besides a database connection)
williamdclt 2 hours ago [-]
`recover` is still useful for unrecoverable errors, eg to capture telemetry then propagate the panic again
troupo 3 hours ago [-]
In most software there's no such thing as unrecoverable panic. OOM is probably the only such error, and even then it doesn't come from within your app.
For all "unrecoverable panics" you usually want to see the reason, log it, kill the offending process, clean up resources, and then usually restart the offending process.
And that's the reason both Go and Rust ended up reverting their stance on "unrecoverable panics kill your program" and introduced ways to recover from them.
Ferret7446 3 hours ago [-]
Go never had a stance on "unrecoverable panics kill your program". Go always supported recover, but encourages (correctly IMO) error values because they are more performant and easier to understand. The Go standard library even uses panic/recover (aka throw/catch) style programming in specific instances.
troupo 2 hours ago [-]
> they are more performant and easier to understand.
They are more performant because Go decided to make them so. E.g. in Erlang crashing a process is an expected lightweight operation.
As for "easier to understand"... They are not when:
- your code is littered with `x, err = ...; if err != nil`
- it's not easier to understand when the code errors have to be dealt with on a higher/different level. The calling code isn't always the one that needs to deal with all the errors
Oh, look, you can't even see the logic behind all the `if err`s which do nothing but return the error to be handled elsewhere.
CharlieDigital 33 minutes ago [-]
You aren't kidding.
Line 143 - 182...
You'd think they'd come up with a short form for something that gets written so often.
skywhopper 3 minutes ago [-]
They haven’t, because it’s perfectly legible, versus languages that hide control flow with implicit assumptions. The main point of Go was to avoid magic. If you don’t care for it, that’s fine. Plenty of us prefer explicit control flow.
zmgsabst 59 minutes ago [-]
I always liked Erlangs “crash until someone knows how to recover”.
shric 2 hours ago [-]
For me an unrecoverable error is when my program gets into an unexpected state. Given that I didn't anticipate such a thing ever happening, I can no longer reason about what the program will do, so the only sensible course of action is to crash immediately.
ThePhysicist 3 hours ago [-]
Do you use recover() a lot? I have never used it much, I guess it is important in some cases but I don't think it's used that much in practice, or is it?
supriyo-biswas 3 hours ago [-]
The only use for me has been to put a recoverer middleware[1] to catch any unhandled panics and return HTTP 500s in my applications.
I've "used" it in pretty much every Go project I've worked on but almost always in the form of an HTTP handle middleware.
Write once, maybe update once a year when we have a change to how we report/log errors.
smnscu 2 hours ago [-]
Having used Go professionally for over a decade, I can count on one hand the times I used recover(). I've actually just refactored some legacy code last week to remove a panic/recover that was bafflingly used to handle nil values. The only valid use case I can think of is gracefully shutting down a server, but that's usually addressed by some library.
commandersaki 1 hours ago [-]
I've seen panic/recover used a lot with recursive descent parsers.
12 minutes ago [-]
MassiveOwl 2 hours ago [-]
I use recover when i'm unsure on how reliable some legacy code is so that we can emit our telemetry and then exit gracefully.
flicaflow 3 hours ago [-]
If I remember correctly, the original actor implementation from scala used exceptions for control flow internally. Blocking on input queues would have blocked a whole thread which doesn't scale for a paradigm which should allow you to run very large numbers of actors. So the exception was used to implement something like light threads. Luckily go solves this problem with go-routines internally.
OutOfHere 39 minutes ago [-]
One of Go's problems, relative to Rust, is that error values of functions can be ignored. In rushed corporate code, this means that developers will inevitably keep ignoring it, leading to brittle code that is not bulletproof at all. This is not an issue in Rust. As for static analyzers, their same use in corporate culture is rare.
Hendrikto 36 minutes ago [-]
You can ignore errors in Rust too, just like any language. And people do, just like with any language.
treyd 18 minutes ago [-]
In Rust you have to explicitly state that you're ignoring the error. There is no way to get the value of an Ok result without doing something to handle the error case, even if that just means panicking, you still have to do that explicitly.
In Go you can just ignore it and move on with the zeroed result value. Even the error the compiler gives with unused variables doesn't help since it's likely you've already used the err variable elsewhere in the function.
You might go with Joshua Bloch and say exceptions are also best reserved for exceptional circumstances (which actually only means "things aren't working as expected"), that's why Go's authors used "panic" instead of "throw" or something similar, to make clear that it shouldn't be used where you might use exceptions in other languages. I mean, it's in the FAQ too: https://go.dev/doc/faq#exceptions
When debugging be it C# or JS, neither the "break on all exceptions" or "break on caught exceptions" are useful on any app. One just hits random library shit, or whatever bloat is in the codebase all the time and the other won't break at all.
But because exceptions are the control flow that is the only way to debug them (or do a human binary search)
Not sure what go debugging is like but I imagine you can quickly work your way to the first err!=nil while debugging.
> quickly work your way to the first err != nil while debugging
I doubt you'll spend any less time debugging in Go. If you disagree, I'd love to see a "side-by-side" comparison for code that's functionally the same but written in both Go and JS, and see some explanations why it's easier in Go
Exceptional circumstances, or exceptions for short, mean "things aren't working as expected due to programmer error". In other words, a situation that theoretically could have been avoided by a sufficiently advanced compiler but that wasn't caught until runtime.
"things aren't working as expected" is vague enough to include errors, which are decidedly not exceptions. One might say a hard drive crash or the network failing isn't working as expected, but those situations are not exceptional.
> to make clear that it shouldn't be used where you might use exceptions in other languages.
Other languages are starting to learn that you shouldn't use exception handlers where you wouldn't use panic/recover, so I'm not sure there is a practical difference here.
Interesting. In Javaland this describes assertions, and the term exception is for operating errors, i.e. problems not necessarily attributable to programmer error, including your example of a network failure.
Because I didn't have much time before the final submission, I just put the initial call in a try catch block and threw an exception to indicate successful completion.
The alternative is every single function has to return a boolean, along with whatever else it would return, which is true when you have found a solution, and you then return straight away -- effectively just reimplementing exceptions by hand, which doesn't feel like a useful use of my time.
I used panic() all day, but never recover. I use panic for unrecoverable errors. I thought that's why it's called "panic".
Restarting like that was faster and more stable than crashing the whole thing and restarting the whole server. But it is a bit dangerous if you don't properly clean up your memory (luckily most APIs are stateless besides a database connection)
For all "unrecoverable panics" you usually want to see the reason, log it, kill the offending process, clean up resources, and then usually restart the offending process.
And that's the reason both Go and Rust ended up reverting their stance on "unrecoverable panics kill your program" and introduced ways to recover from them.
They are more performant because Go decided to make them so. E.g. in Erlang crashing a process is an expected lightweight operation.
As for "easier to understand"... They are not when:
- your code is littered with `x, err = ...; if err != nil`
- it's not easier to understand when the code errors have to be dealt with on a higher/different level. The calling code isn't always the one that needs to deal with all the errors
Just a very random example (I literally just clicked through random files): https://github.com/kubernetes/kubernetes/blob/master/pkg/con...
Oh, look, you can't even see the logic behind all the `if err`s which do nothing but return the error to be handled elsewhere.
Line 143 - 182...
You'd think they'd come up with a short form for something that gets written so often.
[1] https://github.com/go-chi/chi/blob/master/middleware/recover...
In Go you can just ignore it and move on with the zeroed result value. Even the error the compiler gives with unused variables doesn't help since it's likely you've already used the err variable elsewhere in the function.