Should I use panic or return error?

20,269

Solution 1

You should assume that a panic will be immediately fatal, for the entire program, or at the very least for the current goroutine. Ask yourself "when this happens, should the application immediately crash?" If yes, use a panic; otherwise, use an error.

Solution 2

Use panic.

Because your use case is to catch a bad use of your API. This should never happen at runtime if the program is calling your API properly.

In fact, any program calling your API with correct arguments will behave in the same way if the test is removed. The test is there only to fail early with an error message helpful to the programmer that did the mistake. Ideally, the panic might be reached once during development when running the testsuite and the programmer would fix the call even before committing the bad code, and that incorrect use would never reach production.

See also this reponse to question Is function parameter validation using errors a good pattern in Go?.

Solution 3

I like the way it's done in some libraries where on top of a regular method DoSomething, its "panicky" version is added with MustDoSomething. I'm relatively new to go, but I've already seen it in several places, notably sqlx.
In general, if you want to expose your code to someone else, you should either have Must- and a regular version of the method, or your methods/functions should give the client a chance to recover the way they want and so error should be available to them in a go-idiomatic way.
Having said that, I agree that if your API/library is used inappropriately, it's Ok to panic as well. As a matter of fact, I've also seen methods like MustGetenv() that will panic if a critical env.var is missing. Fail-fast mechanism basically.

Solution 4

If some mandatory requirement is not provided or not there while starting the service (eg. database connection, some service configuration which is required) then you should use panic.

There should be return error for any user response or server side error.

Solution 5

Ask yourself these questions:

  • Do you expect the exceptional situation to occur, regardless how well would you code your app? Do you think it should be useful to make the user aware of such condition as part of the normal usage of your app? Handle it as an error, because it concerns the application as working normally.
  • Should that exceptional situation NOT occur if you code appropriately (and somewhat defensively)? (example: dividing by zero, or accessing an array element out of bounds) Is your app totally clueless under that error? Panic.
  • Do you have your API and want to ensure users use it appropriately? Panic. Your API will seldom recover if used incorrectly.
Share:
20,269

Related videos on Youtube

laike9m
Author by

laike9m

Source: https://gumroad.com/l/OcGs

Updated on March 02, 2022

Comments

  • laike9m
    laike9m about 2 years

    Go provides two ways of handling errors, but I'm not sure which one to use.

    Assuming I'm implementing a classic ForEach function which accepts a slice or a map as an argument. To check whether an iterable is passed in, I could do:

    func ForEach(iterable interface{}, f interface{}) {
        if isNotIterable(iterable) {
            panic("Should pass in a slice or map!")
        }
    }
    

    or

    func ForEach(iterable interface{}, f interface{}) error {
        if isNotIterable(iterable) {
            return fmt.Errorf("Should pass in a slice or map!")
        }
    }
    

    I saw some discussions saying panic() should be avoided, but people also say that if program cannot recover from error, you should panic().

    Which one should I use? And what's the main principle for picking the right one?

    • icza
      icza almost 7 years
      Related: Why did Go add panic and recover in addition to error handling? (Not an exact answer to this question, but some reasoning applies here as well.)
    • jeevatkm
      jeevatkm almost 7 years
      For validations use error, here you're checking isNotIterable(iterable). Error would be appropriate.
    • RickyA
      RickyA almost 7 years
      rule of thumb: In the acual main function it is ok to use panics. Errors in all other cases. In libraries that are used in several other programs it is really 'forbidden' to use panic.
    • laike9m
      laike9m almost 7 years
      @RickyA Mind posting an answer?
    • RickyA
      RickyA almost 7 years
      this answers it rather completely
    • dolmen
      dolmen almost 6 years
    • earizon
      earizon over 2 years
      Using err (vs pannic) is mostly a bad idea when developing high level (Layer 7 in OSI model or business like logic) application. Go was designed as system like language (C-like) for OS low level tasks, where err can make sense (program has a lot of context to react to abnormal/non-happy path). Since it has become mainstream outside its targeted area, and applied to business apps, "err" must be replaced by panic. See my updated answer for more info.
  • laike9m
    laike9m almost 7 years
    Agreed. But how do I determine if the program can continue? For me, it seems that if some integer is accidentally passed in, there's no way for the program to continue running.
  • Adrian
    Adrian almost 7 years
    That may be the case. Perhapse "can continue" is a little vague - what it really means is, if the error occurred, is there any way your program could handle the error? Generally speaking, in go, if you're not sure, return an error. Panics should be pretty rare.
  • dolmen
    dolmen almost 6 years
    Define "normal operation". Because that's the point of the question: "is the check isNotIterable about normal operation?"
  • dolmen
    dolmen almost 6 years
    This reply is not an answer: it is a question (Ask yourself...).
  • Adrian
    Adrian almost 6 years
    Read it in its entirety. It encourages the reader to ask themselves a question, in order to offer logic based on the answer to make a decision for themselves.
  • cubuspl42
    cubuspl42 about 5 years
    At the same time, a post from the official blog suggests a different approach. They give the json library as an example, which uses panic internally and converts it to an error in the public API.
  • cubuspl42
    cubuspl42 about 5 years
    I'm not saying it's a good design, just leaving it for reference.
  • dasper
    dasper about 2 years
    I think your answer is deeply flawed and harmful advice. Go allows to fail fast by returning an error as its own value so at any moment you can return with a failure message. The error also allows you to reset to a stable state by inspecting the type of error you receive; even easier than an panic. Where you would use a panic is when you CANNOT recover the flow of control yourself as panic will crash the program if not recovered. If you are recovering immediately up the call stack with no other goroutines operating and a knowledge of why there is an error then you are doing it wrong.
  • earizon
    earizon about 2 years
    @dasper: if you can recorver inmediately up the call then there is no error. You have an standard set of possible values and some of them represent some 'psicologically negative' non-happy values. But a Touring machine can just return a defined value, timeout or fail with undefined behaviour, never with an error. If the error is contemplated it's just part of the well defined set of return values.
  • earizon
    earizon about 2 years
    I completely disagree with this answer. Read my answer for a more complete view of error-vs-exceptions (panics). Most microservices will probably prefer to clasify exceptions into user/input-data exceptions, temporal exceptions and permanent exceptions, then panic and recover in main REPL to provide feedback to users.
  • Adrian
    Adrian about 2 years
    Disagreeing with Go idioms is reasonable, but doesn't change the fact that they're Go idioms, and StackOverflow isn't the place to debate them. In Go, the accepted practice is to expose errors. "the err return value is mostly a wrong idea, even if the GO community has adopted it as a religion" flies in the face of the entire corpus of Go code, and is going to set up anyone following it for failure as a Go developer unless they work exclusively on solo projects.
  • dasper
    dasper about 2 years
    Go's own blog states "The convention in the Go libraries is that even when a package uses panic internally, its external API still presents explicit error return values" so you should use error instead of panic. Also, your logic for using panic is somewhat flawed; should never happen if called correctly is like saying websites should stop sending a 404 error and start crashing the browser because no 404 should ever be issued if people type the URL correctly. If anything APIs and libraries need to be more tolerant of errors in use.
  • Badr Elmers
    Badr Elmers about 2 years
    a question can by itself be an answer, you ask me did you kill John? and I answer: did you see me? if you answer yes, then you already know the answer, if you answer no then how did you know that I killed him? so my question was in fact an answer
  • dolmen
    dolmen almost 2 years
    @dasper Do you check for nil values every time your API takes a pointer/slice/map (even checking method receiver) to report an error? Or do you leave that check to the runtime?