[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)