149 pointsby todsacerdoti8 days ago13 comments
  • rob745 days ago
    > 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

    • aqueueaqueue5 days 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.

      • mrighele5 days 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
        • CharlieDigital5 days ago
          It's the same for the mainstream debuggers for .NET.
          • aqueueaqueue5 days ago
            But if that class is thrown again and again it is less useful which I see in a lot of codebases.

            The go equivalent is "catch the exception when it happens here"

            A debugger feature for that would be nice. I guess it is a debugger concern not an exceptions issue per se.

            • PeeMcGee4 days ago
              Conditional breakpoints exist in Intellij and work well with Java in my experience. You craft a little boolean expression on the breakpoint that references variables in-scope, and the debugger skips the breakpoint unless the expression evaluates to true. Every time I've used this feature has been one of the darkest times of my life.
              • renewiltord4 days ago
                It also slows down execution so much. Agreed that it is a dark day if I'm forced to use that. If it's not in library code I just put the condition in an if and put the breakpoint inside it.
            • CharlieDigital5 days ago
              There is a `justMyCode` option even in VSC: https://code.visualstudio.com/docs/csharp/debugger-settings#...
      • rs1865 days 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

        • BobbyJo5 days ago
          I've shipped code in JS, Python, C++, Java, Golang, and a few others. I can say with certainty Golang was the easiest to debug simply because if a layer could fail, it was explicitly obvious. Exceptions might come from a few layers deep, and you don't know that until one gets raised.
          • rs1864 days ago
            Like I said, an example would be 100% more effective and convincing than your personal experience here.
      • Someone5 days ago
        > Not sure what go debugging is like but I imagine you can quickly work your way to the first err!=nil while debugging.

        How do you imagine that happening? I can’t see another way then either stepping through your code or setting breakpoints on all the ‘return nil, err’ statements. You rarely, if ever, can use a ‘watch variable’ feature, because each function will have its own local ‘err’, and will have a new one on each invocation.

        If, instead of ‘return buil, err’, there’s ‘throw new…’ in such blocks, I don’t see why you couldn’t do the same things.

      • jcelerier5 days ago
        That's very opposite from my experience in c++. Enabling break on throw in gdb or lldb always brings me exactly where I need to be no matter the OS / platform. But the software in c++ pretty much always adheres to "exceptions only for exceptional circumstances" and thankfully let them bubble up without rethrow à la java, otherwise it would be absolutely terrible developer ux
      • 7bit5 days 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.
        • aqueueaqueue5 days ago
          Tell me the dev console option to not do that and I'll use it. I admit it has been a while since I front ended.
      • sapiogram5 days ago
        The go standard library also recovers from panics internally in some places, for example for gradient descent parsers.
    • wetpaws5 days ago
      [dead]
    • 9rx5 days 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.

      • bazoom425 days 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.
        • 9rx5 days ago
          > A crashed harddisk is clearly an exceptional circumstance.

          It is clearly not. It is very much something to anticipate. At sufficient scale, it is guaranteed that it will happen. There is nothing exceptional about it.

          > More specific terms is needed to distinguish errors in the code (eg divide by zero) from unpreventable errors like network failure.

          Luckily we have such terminology already: Exceptions (caused by mistakes in the code) and errors (caused by external faults).

          • daveliepmann4 days ago
            In what context is this terminology standardized?
            • 9rx4 days ago
              Terminology is never standardized. Language doesn't work like that. How, exactly, does one manage to exist in this world and not know that...?

              However, outside of the Java world, which flips the terms around for some reason, this seems to be the prevailing usage. It is also the only usage that makes sense based on what seems to be the prevailing understanding of what "error" and "exceptional" mean. Java's "exceptional conditions" being the ones that you expect to happen regularly doesn't make sense, but was presumably an accident in naming that just so managed to stick.

              Of course, confusion is compounded by some language idioms using exception handling constructs to handle errors.

        • tored5 days ago
          Isn't that what we have exception hierarchies for?
      • daveliepmann5 days 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.

        • ignoramous5 days ago
          > In Javaland this describes assertions, and the term exception is for operating errors, i.e. problems not necessarily attributable to programmer error

          Making matters more confusing in Javaland, Errors are separate from Exceptions, but both are Throwables.

            An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions. The ThreadDeath error, though a "normal" condition, is also a subclass of Error because most applications should not try to catch it.
          
          https://docs.oracle.com/javase/8/docs/api/java/lang/Error.ht... / https://archive.vn/i5F7B
          • PeeMcGee4 days ago
            This reminds me of one of my favorite SO answer-rants that reads like an angry Dr. Seuss rhyme: https://stackoverflow.com/a/36627158

            > Which of these are the "checked" exceptions? Throwables are checked exceptions, except if they're also Errors, which are unchecked exceptions, and then there's the Exceptions, which are also Throwables and are the main type of checked exception, except there's one exception to that too, which is that if they are also RuntimeExceptions, because that's the other kind of unchecked exception.

          • chuckadams5 days ago
            Errors are not checked, the assumption being that most of the time you can’t do anything about them anyway.
      • trallnag5 days ago
        In Java I always see exceptions being used for stuff like validating HTTP requests
  • jjmarr5 days 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.

    • actionfromafar5 days ago
      Enterprise ready. :)
    • CJefferson5 days 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.

      • rollcat5 days ago
        Sounds like Go to me! Every single function that can fail has to return an error value, which is nil if you have succeeded.

        I love Go, but its error handling leaves so much to be desired. I even have an Emacs macro that inserts "if err != nil" for me. Also very easy to make a mistake in the rare case where you have to write "if err == nil"; my eyes just skip over any line that includes the words "if", "err", and "nil", as if it was some attention-hungry inline ad.

        Rust started off chatty, but soon introduced the "?" syntax, which works perfectly well in the 99% case. Go had a couple of similar proposals, but "keep if err != nil" just... won.

        • bheadmaster4 days ago
          I like to use Go error handling for providing curated error messages for each possible point of failure.

          Stack traces in other languages do this in a way, but are often unreadable and require manually inspecting each function in the trace to figure out the reason of the bug. Even worse if some function in the middle catches the original exception and returns another exception, completely devoid of the former's context and information.

          Even worse if you're using any kind of framework that renders stack traces useless ala async Rust or Java Spring. All you get is a bunch of noise from framework and almost nothing from your program.

          In Go, most of the errors I get are chains of carefully written messages, e.g. "request failed: write file: create: directory does not exist". And given that errors are just values and not tied to a stack, they can internally be passed through channels in all kinds of complex goroutine pipelines, and not lose any information.

          Go basically makes your life worse, until you get used to writing meaningful context-relevant messages and thinking about errors. Which in turn makes your life a lot easier when diagnozing and debugging an issue.

        • 9rx5 days ago
          > which works perfectly well in the 99% case.

          Not without its related traits and whatnot. Without those you have the same all the same problems the Go proposals keep running into. Trouble is that it is not yet clear what the Go equivalent is to those related features.

          However, the attempts to figure that out keep coming. Ian Lance Taylor (of generics fame) recently implemented the latest proposal to try it out, although it seems it too has failed, but suggests that things are inching closer to something usable when the initial details were able stand up to basic scrutiny. Nothing has "won" yet.

        • stouset5 days ago
          I remember when Rob Pike wrote an essay insisting that `if err != nil` isn’t all that common and golang programmers clearly would wrap that logic in more fluid interfaces which would prevent the redundant boilerplate.
          • rcxdude4 days ago
            I do wonder what he was imagining that would look like, given go has basically no ways to abstract over control flow.

            (rust, meanwhile, had macros, and people settled on the try! macro fairly quickly, which is basically just bundling up 'if err != nil' into a smaller package, and that eventually turned into '?' because try! was still a bit too unwieldy. Go doesn't have an way to do the same)

            addendum: Having read the blog post, it seems like it's basically arguing for interfaces which essentially buffer error reporting. I've written code that works like this before, and it doesn't really deal with control flow well at all: it's basically only a solution for the case of having a straight-line set of function calls, and some interfaces are commonly used like this, but it's only a fraction of them and you introduce more likelihood of errors if you then try to use them in more complicated situations (imagine his proposed option but you want to make a decision based on a value in the middle, or run a loop. It either stops working entirely or you have to decide on a 'safe' fake default value to return in the case of an error fall-through that doesn't break the logic, which is usually impossible.

          • DangitBobby5 days ago
            • the_gipsy5 days ago
              This reads like satire!
              • stouset5 days ago
                I’ll be honest, having been involved off and on with golang over 10+ years, I constantly feel like I’m being gaslit.

                It’s fine that golang has made missteps along the way and course corrects. Or if people realize that some hoped-for development doesn’t come to pass, but the current state of things is acceptable.

                But the number of times it feels like we’re just told things were always this way, or this was always the goal, or actually having 2/3s of the language being redundant boilerplate is a good thing (it’s “explicit”), or actually they always intended to have generics because it’s important for library authors, or actually systems programming always meant small network services, or actually abstraction is bad unless it’s coincidentally the exact level of abstraction that golang ended up with, or… on and on and on.

                It’s exhausting and it’s not a good look on the community.

                • jub0bs3 days ago
                  > actually they always intended to have generics

                  There was a time when generics were not even on the table. I'm pretty sure I remember an old talk by Rob Pike where he says that much.

                  People can change their mind as they learn more about the problem space; that should be tolerable.

                • mlvljr4 days ago
                  [dead]
              • throwaway20374 days ago
                No trolling here: I am not a Golang programmer. Can you explain more?
                • stouset4 days ago
                  This shows that ten years ago even the guy who invented the language thought error handling wasn’t in a good state and that it was expected that golang programmers would find elegant ways to build on top of the primitives provided.

                  No such development has occurred. The state of the art remains three lines of boilerplate around most function calls in practice.

                  And honestly, if the mindset was: hey, you know what, this isn’t great but it’s what we’ve ended up with, it isn’t the end of the world, and maybe we’ll crack the nut and improve things down the line… I think a lot of golang’s detractors would nod and everyone would move on.

                  Instead, everyone clings to: three lines of mostly-identical error handling around every function call is optimal, I prefer it this way, all of this extra code is “explicit” so it’s an inviolable good, we’ve always been at war with Eastasia.

                  As someone who is more than happy to point out all the things that annoy me about the languages I prefer to use, it feels at times like entire community wants to pretend that there aren’t any flaws or downsides with the language.

                  • throwaway20374 days ago
                    Thanks for the detailed follow-up. I now understand very well what you meant.

                        > it feels at times like entire community wants to pretend that there aren’t any flaws or downsides with the language.
                    
                    Yeah, the delusion of version 2.0 perfection is strong from Golang thought leaders. It is nauseating to read about. Like the whole resistance to generics, then finally giving in, then finally saying "Yeah, we definitely need these." So... the last 8(?) years of blogging against generics... what about that? Another comment below used the phrase "gas-lighting the users" which seems appropriate here.

                    Deeper: This will be controversial to some readers here. I wonder if the reason why the "entire community" resists criticism so fiercely is that Rob Pike has a very public profile and "infinite Internet (reputation) points", so no one can beat that final boss. I'm not saying that Rob Pike would use his Internet boss powers to kill off any critics, but I do think it does repress some critics. Also, his style and opinions are very strong.

                    All said, as a non-Golang writer, but avid reader, I am still very impressed by how carefully that Golang was developed. It could have gone so much worse (C++? JavaScript?), or the community been much more toxic/unwelcoming, e.g., Rust. Overall: It is a huge win for current and future programmers.

                  • 9rx4 days ago
                    > And honestly, if the mindset was: hey, you know what, this isn’t great but it’s what we’ve ended up with, it isn’t the end of the world, and maybe we’ll crack the nut and improve things down the line… I think a lot of golang’s detractors would nod and everyone would move on.

                    What makes you think that? The Go team is clearly working on trying to crack the nut. There is, as of about a month ago, even now an exploratory implementation: https://github.com/golang/go/discussions/71460 It is not an easy nut to crack. Many serious proposals have failed for good reasons. But it is abundantly apparent that the effort is there and ongoing.

                    The "golang detractors" aren't acting rationally. If you watch carefully you will see that most of the time they don't even understand their detraction points, indicating that they are simply repeating what they saw elsewhere. The unfortunate reality is that they aren't looking to contribute anything meaningful, they are simply trying to be funny and they will continue to believe it is funny until "You idiot, Go already has that" can be said and manages to wear them down.

                    > three lines of mostly-identical error handling around every function call is optimal

                    To be fair, while I am sure we could do better, I am not sure we have found it yet. I do my programming in various languages with all these fanciful error handling semantics and it is not good. I regularly wish those languages' idioms had settled on something like if err != nil over what they ended up with. It is not great, but better than the rest. But it is encouraging that smart minds are working hard trying to find better.

                    • stouset4 days ago
                      The golang core team may be working to crack that nut, but right up until that happens the community mantra will be: what we have right now is perfect. When it’s released, this will shortly become: we always wanted something better, it was just a matter of finding what that would be. I’ve seen this happen enough times I’m quite confident that’s the case.

                      The one exception to this trend has been generics. I think it was an inevitable addition to the language, but there are still a lot of holdouts who argue it wasn’t necessary. I think this is probably because it’s more important for library authors than app developers. But it does remain by far the most divisive update to the language.

                      > The "golang detractors" aren't acting rationally. If you watch carefully you will see that most of the time they don't even understand their detraction points, indicating that they are simply repeating what they saw elsewhere.

                      I personally don’t think I’ve seen a lot of that. At this point it’s endemic enough that there’s a small and shrinking number of engineers each year who haven’t spent time in it.

                      > To be fair, while I am sure we could do better, I am not sure we have found it yet.

                      Everything below here is a more or less reasonable take (even if I have mild disagreements), and I think a lot of people would feel less likely to snipe at the language if more people expressed opinions like this.

                      • jub0bs3 days ago
                        I don't think any reasonable member of the Go community would claim that any aspect of the language and its standard library is perfect. The many open issues on https://github.com/golang/go attest to that.

                        One example, if I may: the errors.As function is far from ergonomic. It would have been much better if generics had come to the language before that function was added to the standard library. Modern alternatives exist: https://pkg.go.dev/github.com/jub0bs/errutil

                      • 9rx4 days ago
                        > right up until that happens the community mantra will be: what we have right now is perfect.

                        Where is this mantra emanating from, exactly? Go officially became community driven in 2017. If the community didn't seek this, nobody, especially not the core team, would be working on it. Actions certainly do not support your idea.

                        > there are still a lot of holdouts who argue it wasn’t necessary.

                        Of course. Obviously it isn't necessary, or even beneficial, for all programming problems. The software industry is quite broad and developers are working on all kinds of different things. One is only going to speak from their own experience. It is all they know. And I expect error handling is contentious in much the same way because of it being something that crosses many different types of programming areas in different ways.

                        In fact, despite what may seem contrary to what I said earlier, there are classes of of software problems where you do think about errors differently. For example, letting the program close with a message and letting a human deal with whatever went wrong is a perfect solution to many error-related problems and language constructs that make that easy is wonderful. In those cases the fancy error handling semantics are quite appreciated.

                        Naturally, my assertion that something like "if err != nil" being the best we've got comes from the type of software I normally find myself needing to write. If you work on different types of problems, you're bound to have a different perspective. Expecting to find agreement here is much like expecting basketball players and baseball players to find agreement about what kind of ball they should share. But there is no need to.

                        • stouset4 days ago
                          When I say “the golang community” I mean “the people online who talk about golang”. I don’t mean the actual maintainers.
                          • 9rx4 days ago
                            Meaning specifically the comedians of HN? They say things, just like the “detractor” comedians playing the same game from the opposite side of the coin, for sure, but that comedy community is not representative of anything technical.

                            It's abundantly clear where the actual Go community stands on this. They have a visible interest in seeing what can be done; to try and find out if there is a better way. That does not guarantee success, but the desire to try is undeniable.

      • kccqzy5 days ago
        That's why in Haskell after the Either monad becomes popular, people simply made a library that flips the arguments to become the Success monad.

        The problem with most languages here is the name "exceptions" implying it's for exceptional scenarios, but without any substitute for good non-local control flow.

        • layer85 days ago
          It’s not necessarily only for exceptional scenarios, but instead it is an exception to the normal return of a value. The usage where the return is always by exception (as in the n-queens example) is itself an exception ;). I do think it’s useful to mark one (return) type as the primary/normal/good case and other types as secondary/auxiliary/error cases, and have the latter auto-escalate independently.
      • tcfhgj4 days ago
        "The alternative is every single function has to return a boolean"

        Rust: Return Some(value) or None

        • CJeffersona day ago
          I tried that, the problem was some functions wanted to return an Option, then you end up with Option<Option<T>>, which is technically fine, but I find a bit mind bending.
      • vbezhenar4 days ago
        Another alternative is setjmp/longjmp.
      • kamaal5 days ago
        Its strange that its 2025 and we haven't compiled recursion as a design pattern with rules, like a framework, using which all problems that can be solved by recursion can be represented.
  • shric5 days 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".

    • usrbinbash5 days ago
      > I thought that's why it's called "panic".

      And you are exactly right.

      The problem is: People are so used to the "exceptions" paradigm from other languages, when they see "panic-recover" many immediately think "That's the same thing!!"

      It isn't, because the only VALID usecase for panics is exactly what you describe: unrecoverable error conditions where terminating the program is the best course of action.

      `panic/recover` used like exceptions is an antipattern, and one of the worst code smells in a Go codebase.

      • the_gipsy5 days ago
        You do need to use it, not to handle errors but to avoid it taking down the whole process (and probably sending some logs / alert / monitoring). Which doesn't apply everywhere, but at least in web dev it does: if a request / task panics, you want to abort just that, not the whole server including any other requests / tasks running.

        Sadly, you need every goroutine to have its own recovery handler. This works well for your general request / task entrypoints, as there should only be one for each kind, but you need to watch out for any third-party libs spawning goroutines without recovery. They will take down your whole server.

        • gizzlon5 days ago
          That's not my experience. Other then the recover included in the http lib, I don't think I have ever used recover.

          Why is you code panic'in? I would let it take down the process and figure out why. I have had backend programs set up to automatically restart, which can be useful. But I would treat any panic as a big deal.

          • the_gipsy5 days ago
            You can treat panics as a big deal, and not necessarily kill the whole program. It's not mutually exclusive.

            For example, at my work, we have some nightly long running tasks. We don't panic every day. But from time to time, let's say once or twice per month, some code changes cause a panic. On that day, we don't want to kill the long running tasks for no good reason other than somehow indirectly making someone fix the panic. We have alerts for that, and we're all grownups.

            • usrbinbash5 days ago
              > You can treat panics as a big deal, and not necessarily kill the whole program. It's not mutually exclusive.

              Yes it is mutually exclusive. Something that doesn't kill the program, aka a recoverable ERROR CONDITION should not cause a panic, that's not what panics exist for.

              Something that causes a panic without using the `panic` keyword, like an out-of-bounds read, nil-derference, etc. is indicative of a serious problem that should be fixed before the program is allowed to run again.

              • the_gipsy5 days ago
                > Something that doesn't kill the program, aka a recoverable ERROR CONDITION should not cause a panic

                Can you explain why?

                > a serious problem that should be fixed before the program is allowed to run again

                Can you explain why the program should not be allowed to run again? Is this some sort of software spiritualism?

                • usrbinbash4 days ago
                  > Can you explain why?

                  Because that is semantically what a panic means in Go. See the link to effective go I posted you elsewhere in this thread.

                  I am well aware that it can be used in other ways. Same as I can say "car" when talking about a mainline battle-tank. Sure, a car has an engine, runs on fuel and drives on land. There are similarities. The words still mean very different things.

                  And I am also sure there have been instances of someone using a tank to go order food at a drive-through. Doesn't mean that it is semantically correct to do so, or advisable.

                  • the_gipsy3 days ago
                    Your position is basically "there should be no recover() because you should never recover". Go itself disagrees, no matter how you interpret some "effective go" directive. See how many times it's used in the stdlib:

                    https://github.com/search?q=repo%3Agolang%2Fgo%20recover()&t...

                    It just doesn't make sense to take down the whole server, including all requests / jobs in flight, because there's some nil deref or out-of-bounds. Yea, that thing has to be fixed, but sending a specific alerts is much better than indirectly alerting by taking the whole system down.

                    If you're using go for something non-web, then it may very well make sense to not have recovery anywhere. Except of course you do have some, in the stdlib. But you can apply it to your code, if you want.

                    But it can't be some universal pragma (or convention) in go, as it violates the stdlib.

                    • usrbinbash3 days ago
                      As I have said before, the fact that it is used, doesn't validate the usage. Yes, Go has recover. Yes, the stdlib uses it here and there.

                      Does any of that change the semantics of what a panic means, and how applications should therefore react? No. Does it make panics the equivalent of exceptions in Python semantically? Also no.

                      And this logic isn't limited to Go. Guess what, there are python libraries that use Exceptions for control flow. It certainly works. Does that validate using Exceptions as control flow elements? No, of course not. Why? Because that's not what an exception exists for semantically.

                      Panic-Recover cycles in go codebases are an antipattern, and unless I see an official statement by the people who make Go (who also write "Effective Go" btw.) saying otherwise, those are the semantics of the language.

                • mjevans5 days ago
                  It indicates that either programmer or at least operator (if E.G. this is a critical data validation fail), some sort of human, intervention is required. The situation must be evaluated critically before any further work occurs.
          • allset_4 days ago
            >your code

            3P code is a thing

            >why

            Sometimes there are edge cases with nil pointers that testing missed.

            >automatically restart

            What about all of the other requests in flight? Letting those fail because one request hit an edge case isn't great for a production service with high throughput.

        • usrbinbash5 days ago
          > if a request / task panics

          ...and the condition why it panics is not a situation that warrants a crash, then whatever is called upon handling that request is issueing a panic when it shouldn't.

          The reason why some libs do that anyway is exactly what I describe above: because in many peoples minds panic == exception.

          That's a logic error in the code and should get fixed. And one of the best ways to make devs fix things, is to let their application crash when something that shouldn't happen happens anyway, because then someone will start complaining why a service is unreachable.

          TL;DR:

          If some condition shouldn't crash a process, it has no earthly business causing a panic.

          • the_gipsy5 days ago
            You're conflating unnecessary panics, with how to handle panics.

            There will always be panics. You don't need to crash the thing to make devs notice, they're not idiots no matter what Rob Pike told you. You can alert and not throw out the baby with the bathwater. Nobody wants panics in their code, even if they're not crashing the whole world.

            • usrbinbash5 days ago
              > You're conflating unnecessary panics, with how to handle panics.

              I don't think so. If I have to handle a panic, because otherwise my program no longer works, one of 2 things is true in the vast majority of cases:

              - There is something seriously wrong with the program or its environment, causing it to panic

              - There is something in the program issueing a panic when really it should return an error

              In short: there should be no need to "handle panics"

              Panics are irrecoverable conditions where its preferable for the program to crash rather than continue. If code panicks for any other reason, thats, in my opinion, wrong, and should be fixed. Panics are not the equivalent to exceptions, and error returns exist for a reason.

              People who don't like that paradigm can always use a language that uses the exception-paradigm.

              • the_gipsy5 days ago
                > People who don't like that paradigm can always use a language that uses the exception-paradigm.

                FYI the go std library recovers from panics when it spawns goroutines internally, in most cases.

                All this has next to nothing to do with exceptions. Nobody is saying to use panics to pass errors or any control flow.

      • lblume5 days ago
        Exactly, same as panic! in Rust.

        There is a reason Rust was reluctant to add std::panic::catch_unwind at first. The docs thus explicitly mention that (1) it is not a typical exception mechanism and (2) that it might not even catch panics if unwinding is disabled (common for embedded and restricted development).

      • vbezhenar4 days ago
        panic/recover is the same thing as exceptions. You can avoid them if you want, that's your decision, doesn't change the technical fact.
        • usrbinbash4 days ago
          No it isn't. Semantics matter, and using something against the defined semantics of the language is a huge code smell.

          For example, I could ignore the fact that Python has exceptions, and instead let functions return error values.

          Would that work? Yes, absolutely, and I have seen Py-Codebases that do this.

          Is it semantically correct? No, because in python, semantics dictate that error states are handled via exceptions, and that is the expectation everyone has when opening a python codebase.

          When in Rome, do as the Romans do.

          • xerokimoa day ago
            That's being idiomatic to a language, not being semantically correct.

            Result, Either, Expected, all have different names, but their semantics are all the same.

            Panic and Recover may not be idiomatically used the same way Exceptions are used in other languages, but they share the exact same semantics of implicitly bailing out potentially multiple functions, going up the call stack until we Catch, or well Recover.

    • williamdclt5 days ago
      `recover` is still useful for unrecoverable errors, eg to capture telemetry then propagate the panic again
    • stouset5 days ago
      Sometimes you don’t even want to recover, just do something like log it remotely so it can be seen and debugged.

      Sometimes it can’t reasonably be handled until some natural boundary. A server that handles multiple connections at once can produce an unrecoverable error handling one of those connections, but it still should gracefully close the connection with the appropriate response. And then not kill the server process itself.

    • DanielHB5 days 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)

      • bilekas5 days ago
        Maybe I’m mistaken but isn’t that a hand crafted attack vector to taking down your services?
        • knome5 days ago
          hopefully, anything triggering a panic should be an exceptional case that the caller cannot purposefully put the server into. restarting the app without a full process reload shouldn't be any more of an attack vector for denial of service than restarting the entire app.
    • tgv5 days 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.
      • gizzlon5 days ago
        IIRC, the Must.. functions are typically used at program start, and in cases where you would like the program to stop. At least that's the way I've used it.

        For example to read and parse expected templates from disk. If they aren't there, there really is no reason to continue, it's just very very confusing.

        https://pkg.go.dev/html/template@go1.24.0#Must

      • arccy5 days ago
        the template and regexp packages have Must variants that panic. They're intended to crash the program on init because of programmer error.
    • amanj415 days ago
      I think a good usecase for recover is in gRPC services for example. One wouldn't want to kill the entire service if some path gets hit leading to a panic while handling one request.
      • closeparen5 days ago
        Corporate gRPC services are written with "if err != nil" for every operation at every layer between the API handler and the db/dependencies, with table-driven tests mocking each one for those sweet sweet coverage points.

        I would love a community norm that errors which fail the request can just be panics. Unfortunately that's not Go as she is written.

        • GeneralMayhem4 days ago
          One thing that `if err != nil { return err }` lets you do, which panic/recover doesn't, is annotate errors with context. If you're throwing from 5 layers deep in the call stack, and two of those layers are loops that invoke the lower layers for each element of a list, you probably really want to know which element it was that failed. At that point, you have two options:

          1. Pass a context trace into every function, so that it can panic with richer meaning. That's a right pain very quickly.

          2. Return errors, propagating them up the stack with more context:

            for i, x := range listOfThings {
              y, err := processThing(x)
              if err != nil {
                return fmt.Errorf("thing %d (%s) failed: %w", i, x, err)
              }
            }
          • usrnm4 days ago
            You can add arbitrary information by catching and rethrowing exceptions (which go panics, basically, are).
            • GeneralMayhem4 days ago
              Go panic isn't really usable as catch/rethrow because it can only be done at function scope. To make them useful for that pattern, you need a scoped `try { }` block where you can tell what part failed and continue from there. Either that, or you need lots and lots of tiny functions to form scopes around them.
              • usrnm3 days ago
                You don't need "lots and lots" of tiny functions, most of the time it's totally fine to just let the exception propagate as is. When you do need to add information, you will have to use a function, yes, it's an unfortunate feature of golang. Same with defers, the only way to scope them is to wrap them in a function, it's stupid, but this is golang for you
          • closeparen4 days ago
            That’s mostly just a stacktrace. You can add other information that wouldn’t be in a stacktrace, but you shouldn’t do it by string concatenation because then every instance of the error is unique to log aggregators. Instead, you need to return a type implementing the error interface. Which is not all that different from throwing a subclass of exception.
          • fireflash384 days ago
            I mean yes, but I don't really like hand writing exception tracebacks via error wrapping.

            That said... I did like a clever bit I did where you can use a sentinel error to filter entire segments of the wrapped errors on prod builds. A Dev build gives full error stacks.

        • amanj413 days ago
          Yes that is common. I was more talking about the case where someone perhaps introduces a bug causing a nil pointer dereference on some requests, so the panic is not explicitly called in code. In which case you would definitely want the recover in place.
          • jub0bs3 days ago
            Instead of recovering from what clearly is a bug, why not fix that bug instead?
      • seanw4445 days ago
        Some are of the opinion that that should be handled a layer up, such as a container restart, because the program could be left in a broken state which can only be fixed by resetting the entire state.
        • oefrha5 days ago
          Given that you can’t recover from panics on other goroutines, and Go makes it extremely easy to spawn over goroutines, often times it’s not even an opinion, you have to handle it a layer up. There’s no catchall for panics.
          • __turbobrew__4 days ago
            This is a major pain in the ass. I was trying to solve the problem of how do you emit a metric when a golang service panics, the issue is that there is no way to recover panics from all goroutines so the only way to do that reliably is to write a wrapper around the ‘go’ statement which recovers panics and reports them to the metrics system. You then have to change every single ‘go’ call in all of your code to use this wrapper.

            What I really want is either a way to recover panics from any goroutine, or be able to install a hook in the runtime which is executed when an unhandled panics occurs.

            You can kind of fudge this by having the orchestration layer look at the exit code of the golang process and see if it was exit code 2 which is usually a panic, but I noticed that sometimes the panic stack trace doesn’t make it to the processes log files, most likely due to some weird buffering in stdout/stderr which causes you to lose the trace forever.

    • troupo5 days 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.

      • Ferret74465 days 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.
        • troupo5 days 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

          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.

          • CharlieDigital5 days 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.

            • williamdclt5 days ago
              Simplicity and explicitness are really important to Go design. It's been defended hotly. Personally (using Go everyday) I think it's a terrible trade-off for devx and is mostly stockholm syndrom, but it's very difficult to argue at the "design principle" level: one side says "explicitness!", the other says "readability!", which can't be meaningfully compared.
              • CharlieDigital4 days ago
                I feel like both of these are still pretty explicit but much better DX:

                    return when err
                    return if err
                    return on err
            • skywhopper5 days 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.
              • williamdclt5 days ago
                Everyone prefers explicit control flows. But there's other things in the balance (noise, code density, readability), and many people think that this is pretty bad trade-off. Rust with Optionals strikes a much nicer trade-off, and personally I'd love to see a language that offers a good checked exception mechanism which would offer a lot of explicitness with most noise removed.
              • troupo5 days ago
                It would be "perfectly legible" if it was just the logic. As it is, it's more `if err return err` than actual logic.

                And there's high chance the layer above is doing the same thing, and the layer above, until you actually get to actual error handling.

                Rust at least recognized this and first provided try? and then the ? operator as a shortcut to this boilerplate

              • CharlieDigital5 days ago
                How about a simple one liner like (just throwing out random ideas; I'm sure these are not idiomatic)

                    return when err
                    return if err
                    return ?> err
                    return ? err
                
                Still readable, still pretty explicit IMO.
                • vbezhenar4 days ago
                  `return err` is a terrible approach. You must wrap the error with new error, include file and line information, so stacktrace is actually useful. When program spews some random garbage into its stderr and you have no idea where that message came from (imagine looking through millions of lines of kubernetes implementation), that's the good way to ensure that nobody would even care to look at it. Thousands of unsolved issues in kubernetes tracker is a perfect demonstration of it.

                  Go is just bad at handling errors. Exceptions are superior in every way.

                  • CharlieDigital4 days ago
                    Even that could be fixed right?

                        return when err
                        return if err
                        return ?> err
                        return ? err
                    
                    to:

                        return when stackify(err)
                        return if stackify(err)
                        return ?> stackify(err)
                        return ? stackify(err)
                    
                    I don't know; I'm just throwing ideas out there.

                    I do agree that exceptions are just fine and it's not like you couldn't just catch and return exceptions if you wanted to like err.

                    • vbezhenar3 days ago
                      They definitely could introduce a shorter syntax like Rust did. And they discussed that, but apparently they were not happy with ideas so far, so it's postponed. May be some day.
            • kevin_thibedeau5 days ago
              Fix that with m4.
          • zmgsabst5 days ago
            I always liked Erlangs “crash until someone knows how to recover”.
      • usrbinbash5 days ago
        > In most software there's no such thing as unrecoverable panic

        Webserver wants to start, binding port 443/80 isn't possible because another process holds that port.

        Logging service wants to write to disk. The IO operation fails.

        RDBMS want's to access the persistent storage, the syscall fails due to insufficient permissions.

        How are any of those recoverable?

        • troupo5 days ago
          Note how none of these issues should cause the respective programs to crash, as this is what `panic` does.

          They try to start, cannot do a specific operation, and they do an orderly shutdown. Or they should

          • rcxdude4 days ago
            Orderly shutdown is overrated. I would prefer they have mechanisms to deal with a crash and use them by default so they're actually tested. And certainly the process should return an error code in such a failure to startup.
          • usrbinbash5 days ago
            > and they do an orderly shutdown. Or they should

            Which is exactly what panic does.

            • troupo5 days ago
              --- start quote ---

              Panic is a built-in function that stops the ordinary flow of control and begins panicking... The process continues up the stack until all functions in the current goroutine have returned, at which point the program crashes.

              --- end quote ---

              This is far from orderly. For example, what happens to other goroutines?

              I also like how golang docs literally describe using panics as poor man's exceptions:

              --- start quote ---

              For a real-world example of panic and recover, see the json package from the Go standard library. It encodes an interface with a set of recursive functions. If an error occurs when traversing the value, panic is called to unwind the stack to the top-level function call, which recovers from the panic and returns an appropriate error value

              ...

              The convention in the Go libraries is that even when a package uses panic internally, its external API still presents explicit error return values.

              --- end quote ---

              • usrbinbash5 days ago
                > I also like how golang docs literally describe using panics as poor man's exceptions:

                And I like how https://github.com/golang/go/issues/26799 describes that use of panic in the original version of this bog entry from 2010:

                quote:

                However, this is a poor example and a misuse of panic and recover as exceptions. See https://golang.org/doc/effective_go.html#panic

                The example will be invalid soon with the release of go 1.11 as well. The panic/recover was removed from the unmarshal code. See master's

                end quote.

                The blog entry was later changed because this was fixed. It now refers to marshaling which, sadly, sill uses this mechanism.

                The fact that this is still in the json package is a pain point, yes. Does it validate the use of panic as a form of flow control? No. Here is what "Effective Go" has to say about the topic:

                https://go.dev/doc/effective_go#panic

                quote:

                But what if the error is unrecoverable? Sometimes the program simply cannot continue.

                For this purpose, there is a built-in function panic that in effect creates a run-time error that will stop the program (but see the next section).

                end quote.

                • troupo5 days ago
                  The question of orderly shutdown still remains
                  • usrbinbash4 days ago
                    As another user has already mentioned, deferred functions run even when a panic unwinds the stack.

                    And semantically, a panic doesn't even need an orderly shutdown. Again: A panic should ONLY be issued if the application enters a state where continuation of normal operation is not possible, and/or may even be ill advised. "Orderly" operations are, by definition, no longer possible at this point.

                  • arccy5 days ago
                    deferred functions still run, that's the shutdown.
      • shric5 days 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.
      • bilekas5 days ago
        I have to add the obligatory “you’ve solved the halting problem” question here.
        • troupo5 days ago
          No idea what this has to do with the halting problem
          • bilekas3 days ago
            > In most software there's no such thing as unrecoverable panic

            > The halting problem is the problem of determining, from a description of an arbitrary computer program and an input, whether the program will finish running

            They’re very much related to determining you’re gonna panic or not.

  • donatj4 days ago
    I write a pretty significant amount of Go code for my day job, and I have written code that calls panic probably less than five times.

    There are only really two types of cases where I would even consider it an option.

    Firstly, cases where I am handling an error that should never ever happen AND it is the only error case of the function call such that eliminating it removes the need for an error on the return.

    The other case is where I have an existing interface without an error return I need to meet and I have a potential error. This is the result of bad interface design in my opinion but sometimes you have to do what you have to do,

    • KingOfCoders4 days ago
      I only use panic during startup on unrecoverable errors, can't read credentials for example.
    • hanikesn4 days ago
      How often did you access a nil pointer and caused a panic?
  • ThePhysicist5 days 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-biswas5 days 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.

      [1] https://github.com/go-chi/chi/blob/master/middleware/recover...

    • smnscu5 days 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.
    • rgallagher275 days ago
      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.
    • colonial5 days ago
      At least in Rust (which has an effectively identical API here) the only reasonable use case I've seen is as a "last resort" in long-running programs to transform panicking requests into a HTTP 500 or equivalent.
    • goodoldneon5 days ago
      I always make sure recover is used in all goroutines I start. I don’t want a panic in a random goroutine to crash my whole server
    • lelandbatey5 days ago
      I've only ever had to use it in certain complex cleanup mechanisms (certain transaction handling, request middleware, etc)
    • commandersaki5 days ago
      I've seen panic/recover used a lot with recursive descent parsers.
  • peterohler4 days ago
    My experience is quite a bit different. Of course the examples I would use are more like what you might expect in real code. The comparison should be against code that calls a function that either returns and error and checks that error or one that panics and recovers. The overhead of returning the extra error and then the conditional used to check that error is more than a panic on error and recovery somewhere up the stack. This was not true in the early days of go but it is true today.

    It really depends on the code being written. Try one approach then the other and see if it works better in your situation. For the example in the article there is really no need for an error check in the idiomatic case so why compare that to using panic. If there was an error to check the result would be much different.

  • OutOfHere5 days 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 sane use in corporate culture is rare.
    • Hendrikto5 days ago
      You can ignore errors in Rust too, just like any language. And people do, just like with any language.
      • treyd5 days 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.

        • foobarbaz335 days ago
          True, but running errcheck will catch cases where you accidentally ignore the error. Maybe not as good as having it built-in to the language like Rust, but the goal of error check safety is achieved either way.

          And there's a few cases like Print() where errors are so commonly ignored you don't even want to use the "_" ignore syntax. Go gives you the poetic license to avoid spamming underscores everywhere. error linters can be configured to handle a variety of strictness. For non-critical software, it's OK to YOLO your Print(). For human-death-on-failure software you may enforce 100% handling, not even allowing explicit ignore "_" (ie become even stricter than Rust language default)

          • sapiogram5 days ago
            errcheck will not catch everything. For example, it will not stop you from using a value before checking the error, as long as you check the error later. I've personally broken things in production because of that corner case, despite (usually) being very careful about error handling.
            • Cthulhu_5 days ago
              No language or linter will be perfect, and IMO unit tests should be written for what the compiler doesn't do for you. In this case, your unit tests missed code paths, which should show up in a code coverage analysis or fuzz test.
              • treyd5 days ago
                Rich type systems like Rust's do completely prevent this type of mistake. You don't need unit tests to ensure that the error condition is handled somehow because we statically assert it within the type system of the language.
                • 9rx5 days ago
                  Handling an error is pointless if you don't handle it correctly, and for that you need unit tests anyway, so you still need to write the tests and once you've done that it is impossible to encounter the mistake without knowing it no matter how you slice it.

                  But your editor can give some more realtime feedback instead of waiting for your tests to run, so I guess that's cool.

                  • the_gipsy5 days ago
                    You need to give it a try
                    • 9rx5 days ago
                      Give improperly handling an error a try? I’m good. I prefer my software to function correctly.
              • sapiogram5 days ago
                In Rust it's literally impossible to use a returned value without checking the error. This bug also cannot happen with Java, C# or Javascript exceptions. This particular failure mode is unique to Go.
                • simonask4 days ago
                  It's not unique - C has it, and error-code-flavored C++ can emulate it too. ;-)
          • OutOfHere5 days ago
            I would think it's the job of the static analyzer to ignore errors for print. Unfortunately, the proper and consistent use of an appropriate static analyzer is not common in typical rushed corporate settings. Rust avoids this dilemma by enforcing it at the compiler level. There are numerous other types safety reasons too why Rust is safer for careless teams.
            • Cthulhu_5 days ago
              I really wouldn't object if some of the rules in the Go world currently living in the broad linter space were to be moved to the compiler. To a point Go's ecosystem has some reasonable defaults but it could be a bit stricter I think.
        • bigstrat20035 days ago
          That's only the case if you consume the return value. It's perfectly legal (though it gives you a compiler warning) to call a function that returns a Result and never check what actually happened.
    • Cthulhu_5 days ago
      The question that's needed to ask is whether you'd like the language or its ecosystem to guard against these things, or whether you are a decent and disciplined developer.

      For example, Go's language guards against unused variables or imports, they are a compiler error. Assigning an `err` variable but not using it is a compiler error. But ignoring the error by assigning it to the reserved underscore variable name is an explicit action by the developer, just like an empty `catch` block in Java/C# or the Rust equivalent.

      That is, if you choose to ignore errors, there isn't a language that will stop you. Developers should take responsibility for their own choices, instead of shift blame to the language for making it possible.

      • TheDong5 days ago
        > For example, Go's language guards against unused variables or imports, they are a compiler error. Assigning an `err` variable but not using it is a compiler error.

        Unfortunately, Go's language design also enables unused variables without any error or warning. They are only sometimes a compiler error.

        Specifically, multiple return interacts poorly with unused variable detection. See:

            func fallable() (int, error) {
               return 0, nil
            }
        
            func f1() {
               val, err := fallable()
               if err != nil { panic(err) }
               fmt.Println(val)
               val2, err := fallable()
               fmt.Println(val2)
               // notice how I didn't check 'err' this time? This compiles fine
            }
        
        When you use `:=` it assigned a new variable, except when you do multiple return it re-assigns existing variables instead of shadowing them, and so the unused variable check considers them as having been used.

        I've seen so many ignored errors from this poor design choice, so it really does happen in practice.

      • lexicality5 days ago
        Whenever I see people appealing to developers to be "disciplined" I think about those factory owners protesting that they wouldn't need guards or safety rails if their workers were just more careful about where they walked.

        If developers were more disciplined Go wouldn't need a garbage collector because everyone would just remember to call `free()` when they're done with their memory...

      • amenhotep5 days ago
        Rust genuinely will stop you, though. You can't take a Result<T> and get an Ok(T) out of it unless there's no error; if it's an Err then you can't continue as if you have a T.

        It doesn't force you to do something productive with the error, but you can't act like it was what you wanted instead of an error.

        • TheDong5 days ago
          Yeah, but now you have to teach your programmers about generics and applicative functors.

          I'm just a simple country gopher, but my brain isn't capable of understanding either of those things, nor are any of my coworkers, nor any of the people we hire, and it doesn't really matter how theoretically "nice" and "pure" your generics and results and monads are if us real professional programmers get confused by them.

          Errors need to be explicit values, not magic monads. 'if err != nil' is good and easy to think about, 'res.map().or_else()' is incomprehensible line noise no normal programmer can understand.

          https://paulgraham.com/avg.html#:~:text=The%20Blub%20Paradox

          • amenhotep4 days ago
            I think you have ideas about these types that make them seem overcomplicated! Either that, or I'm way smarter than the average programmer and really am massively wasting my talent :) but if you ask me the first is much more likely. I'm awed by the clever things people post about on here and am still not 100% sure I understand what a monad even is, but Result and Option seemed very easy to pick up.

            The one liner functor stuff in particular I think is a big distraction. To me the basic building block is the match statement - you match on a result, if it's Ok(T) then you get to do something with T, and if it's Err(e) then you get to do something with e. At that level it's very simple. And as far as I know (no expert though) any clever expression can be decomposed into a series of nested and possibly repetitive match statements, the apparent magic is illusory.

            But I do hear you and I get the appeal of Go from that angle, obviously sticking with what you like is a good idea. Just saying Rust isn't that bad :)

      • dontlaugh5 days ago
        However, you can make a call without assigning the result at all, entirely ignoring it. That isn’t possible in Rust.
    • crackrook4 days ago
      In my view the core problem is the lack of proper sum types. Using product types to represent results seems fundamentally wrong; the vast majority of procedures only have 2 possible outcomes -- they fail or they don't -- 2 of the 4 outcomes that the type system permits are (almost always) nonsensical. I don't see a good reason to design a modern language in this way, it feels less like an intentional design choice and more like a hack.
    • liampulles4 days ago
      I think the issue here is not so much that errors can be ignored in Go (sometimes one doesn't care if something worked, they just want to try), it's more that errors can easily be ignored by accident in Go.

      I definitely have seen that, and sometimes have done that myself, but I have to say it hasn't happened in a while since the linting tools have improved

    • Thaxll4 days ago
      Rust values can be ignored as well.

      There is no language to my knowledge that prevent you from ignoring values.

      • k_g_b_4 days ago
        Any language that implements linear types has values that can't be ignored and need to be passed to some "destructor" (could just be a deconstruction pattern assignment/similar).

        Examples: Austral, some of these https://en.m.wikipedia.org/wiki/Substructural_type_system#Pr... in particular at least ATS, Alms, Granule, LinearML Though most haven't gone beyond research languages yet.

  • __turbobrew__4 days ago
    Here are my rules of thumb:

    1. Only panic in the top level main() function. Bubble up all errors from sub functions and libraries to the top level function and then decide what to do from there.

    2. If you want to offer a library function which can panic on errors, create two versions of the function: one which returns an error, and one which panics on an error and has a name which starts with ’Must’. For example Load() returns an error and MustLoad() doesn’t return an error and instead panics on error.

  • enjoylife5 days ago
    Only at the very end does the article call out there is actually a performance aspect if you use panic and recover as intended.

    > So it seems that panic and recover can be beneficial to performance in at least some situations.

    Namely top level & centralized recovery handling. I'll also point out its important your panic recovery must happen in a deferred function if your kicking off new goroutines. For example, your server library probably has default panic recovery on the goroutines handling inbound requests, but any new goroutines you create as part of handling the request (e.g. to parallelize work), will not have this handling built in. See https://go.dev/blog/defer-panic-and-recover for more.

  • MassiveOwl5 days 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.
  • flicaflow5 days 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.
  • L-four5 days ago
    Writing transaction code wrapping a user provided function which can error or panic is a real pain to get right. If the user code panics there is no error if the user code succeeded there is no error. So you have to call recover on success.
  • 5 days ago
    undefined