[HN Gopher] The cost of Go's panic and recover
       ___________________________________________________________________
        
       The cost of Go's panic and recover
        
       Author : todsacerdoti
       Score  : 91 points
       Date   : 2025-03-01 08:19 UTC (3 days ago)
        
 (HTM) web link (jub0bs.com)
 (TXT) w3m dump (jub0bs.com)
        
       | jjmarr wrote:
       | 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.
        
         | actionfromafar wrote:
         | Enterprise ready. :)
        
         | CJefferson wrote:
         | 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.
        
           | kamaal wrote:
           | 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.
        
           | rollcat wrote:
           | 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.
        
             | 9rx wrote:
             | _> 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.
        
       | flicaflow wrote:
       | 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.
        
       | ThePhysicist wrote:
       | 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 wrote:
         | 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...
        
         | smnscu wrote:
         | 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 wrote:
         | I've seen panic/recover used a lot with recursive descent
         | parsers.
        
         | rgallagher27 wrote:
         | 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.
        
         | colonial wrote:
         | 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.
        
       | shric wrote:
       | 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".
        
         | troupo wrote:
         | 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 wrote:
           | 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 wrote:
             | > 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/kubern
             | etes/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.
        
               | zmgsabst wrote:
               | I always liked Erlangs "crash until someone knows how to
               | recover".
        
               | CharlieDigital wrote:
               | 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 wrote:
               | 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.
        
               | williamdclt wrote:
               | 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.
        
               | CharlieDigital wrote:
               | 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.
        
               | troupo wrote:
               | 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
        
               | williamdclt wrote:
               | 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.
        
           | shric wrote:
           | 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.
        
           | bilekas wrote:
           | I have to add the obligatory "you've solved the halting
           | problem" question here.
        
             | troupo wrote:
             | No idea what this has to do with the halting problem
        
           | usrbinbash wrote:
           | > 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?
        
             | troupo wrote:
             | 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
        
               | usrbinbash wrote:
               | > and they do an orderly shutdown. Or they should
               | 
               | Which is exactly what panic does.
        
               | troupo wrote:
               | --- 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 ---
        
               | usrbinbash wrote:
               | > 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.
        
               | troupo wrote:
               | The question of orderly shutdown still remains
        
         | williamdclt wrote:
         | `recover` is still useful for unrecoverable errors, eg to
         | capture telemetry then propagate the panic again
        
         | DanielHB wrote:
         | 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)
        
           | bilekas wrote:
           | Maybe I'm mistaken but isn't that a hand crafted attack
           | vector to taking down your services?
        
             | knome wrote:
             | 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.
        
         | tgv wrote:
         | 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.
        
           | gizzlon wrote:
           | 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
        
         | usrbinbash wrote:
         | > 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.
        
           | lblume wrote:
           | 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).
        
             | kmeisthax wrote:
             | Also (3) there are panics that can't be safely caught, such
             | as stack overflow.
        
           | the_gipsy wrote:
           | 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.
        
             | usrbinbash wrote:
             | > 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_gipsy wrote:
               | 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.
        
               | usrbinbash wrote:
               | > 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.
        
             | gizzlon wrote:
             | 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_gipsy wrote:
               | 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.
        
               | usrbinbash wrote:
               | > 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.
        
         | amanj41 wrote:
         | 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.
        
           | seanw444 wrote:
           | 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.
        
       | rob74 wrote:
       | > _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
        
         | 9rx wrote:
         | _> 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.
        
           | bazoom42 wrote:
           | 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 wrote:
             | Isn't that what we have exception hierarchies for?
        
             | 9rx wrote:
             | _> 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).
        
           | daveliepmann wrote:
           | >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.
        
             | ignoramous wrote:
             | > _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, _Error_ s are
             | separate from _Exception_ s, but both are _Throwable_ s.
             | 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.h
             | t... / https://archive.vn/i5F7B
        
               | chuckadams wrote:
               | Errors are not checked, the assumption being that most of
               | the time you can't do anything about them anyway.
        
           | trallnag wrote:
           | In Java I always see exceptions being used for stuff like
           | validating HTTP requests
        
         | aqueueaqueue wrote:
         | 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.
        
           | rs186 wrote:
           | 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
        
             | BobbyJo wrote:
             | 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.
        
           | mrighele wrote:
           | 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 wrote:
             | It's the same for the mainstream debuggers for .NET.
        
               | aqueueaqueue wrote:
               | 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.
        
           | 7bit wrote:
           | 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.
        
           | sapiogram wrote:
           | The go standard library also recovers from panics internally
           | in some places, for example for gradient descent parsers.
        
           | jcelerier wrote:
           | 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 a la java, otherwise it would be absolutely
           | terrible developer ux
        
           | Someone wrote:
           | > 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.
        
       | MassiveOwl wrote:
       | 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.
        
       | OutOfHere wrote:
       | 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.
        
         | Hendrikto wrote:
         | You can ignore errors in Rust too, just like any language. And
         | people do, just like with any language.
        
           | treyd wrote:
           | 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.
        
             | foobarbaz33 wrote:
             | 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)
        
               | sapiogram wrote:
               | 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_ wrote:
               | 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.
        
               | treyd wrote:
               | 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.
        
               | 9rx wrote:
               | 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.
        
               | OutOfHere wrote:
               | 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_ wrote:
               | 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.
        
         | Cthulhu_ wrote:
         | 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.
        
           | lexicality wrote:
           | 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...
        
           | dontlaugh wrote:
           | However, you can make a call without assigning the result at
           | all, entirely ignoring it. That isn't possible in Rust.
        
           | amenhotep wrote:
           | 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.
        
             | TheDong wrote:
             | 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%20Parado
             | x
        
           | TheDong wrote:
           | > 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.
        
       ___________________________________________________________________
       (page generated 2025-03-04 23:00 UTC)