FAQ
An article in the official Go blog suggests to use panic() only internally
in packages:

"The convention in the Go libraries is that even when a package uses panic
internally, its external API still presents explicit error return values."
http://blog.golang.org/2010/08/defer-panic-and-recover.html

But there are counterexamples like
http://golang.org/pkg/regexp/#MustCompile So using panic should be ok, even
in public functions, if there is no good way to continue work:

"One possible counterexample is during initialization: if the library truly
cannot set itself up, it might be reasonable to panic, so to speak."
http://golang.org/doc/effective_go.html#panic

My extract from this is: *use panic() for programming errors and otherwise
error return values.* I've written a simple router that must be configured
with a kind of regex: router.Register("/<city>/<street>", myHandler) If the
pattern in the first parameter is wrong, panic() is called. Do you agree
with this usage of panic?

Another idea to use panic() is parameter checking like it is common in Java
(http://code.google.com/p/guava-libraries/wiki/PreconditionsExplained).
That could reduce the debugging time and lead to more robust code without
the error handling code in trivial cases. Example:

func Register(path string, handler Handler) {
if path == "" {
panic("path must not be empty")
}
...
}

What do you think of this kind of parameter checking?

panic() takes an empty interface as parameter and is usally called with a
string. When would be useful to pass something else? And how can the
developer get a useful error message then?

Regards
Christian

--

Search Discussions

  • Chris Niekel at Nov 22, 2012 at 11:03 am
    If you take regexp as an example, you could have router.Register that
    returns an error, and a router.MustRegister(...) that panics.

    As a precondition-failure, I'd return an error. That's like fmt.Sscanf with
    a "%" pattern.

    On Thu, Nov 22, 2012 at 11:28 AM, Christian wrote:

    An article in the official Go blog suggests to use panic() only internally
    in packages:

    "The convention in the Go libraries is that even when a package uses panic
    internally, its external API still presents explicit error return values."
    http://blog.golang.org/2010/08/defer-panic-and-recover.html

    But there are counterexamples like
    http://golang.org/pkg/regexp/#MustCompile So using panic should be ok,
    even in public functions, if there is no good way to continue work:

    "One possible counterexample is during initialization: if the library
    truly cannot set itself up, it might be reasonable to panic, so to speak."
    http://golang.org/doc/effective_go.html#panic

    My extract from this is: *use panic() for programming errors and
    otherwise error return values.* I've written a simple router that must be
    configured with a kind of regex: router.Register("/<city>/<street>",
    myHandler) If the pattern in the first parameter is wrong, panic() is
    called. Do you agree with this usage of panic?

    Another idea to use panic() is parameter checking like it is common in
    Java (http://code.google.com/p/guava-libraries/wiki/PreconditionsExplained).
    That could reduce the debugging time and lead to more robust code without
    the error handling code in trivial cases. Example:

    func Register(path string, handler Handler) {
    if path == "" {
    panic("path must not be empty")
    }
    ...
    }

    What do you think of this kind of parameter checking?

    panic() takes an empty interface as parameter and is usally called with a
    string. When would be useful to pass something else? And how can the
    developer get a useful error message then?

    Regards
    Christian

    --

    --
  • Peter at Nov 22, 2012 at 11:04 am
    I think you've got the right idea. The only alternative I've seen is using
    panic as a kind of multi-level recursive break (and I haven't done so as
    that just sounds complicated...).

    regexp.MustCompile is intended for initialisation, usually with a string
    literal. If called during init() it acts as almost like a second
    compile-phase, to sanity check your regexes.

    If your router is intended to be initialised once, with Register being
    called with string literals as in your example, I think a panic works well.
    If you're going to call Register throughout your program, with computed or
    user-given strings, I suggest using an error value.

    I don't do the kind of parameter checking you suggest, as it just clutters
    the code too much for the benefits it brings (IMO). But various sanity
    checks are a good idea, in general, with panic a good way to alert you of
    them.

    Panic can be called with an error type, which is anything with an Error()
    string method. Examples of how you can use errors to contain important
    information are here http://golang.org/doc/articles/error_handling.html,
    the same principles apply to panics.

    As always in programming, there's no hard rule, but I believe this is the
    general consensus on how panic() is meant to be used. (Please correct if
    I'm mistaken).
    On Thursday, 22 November 2012 10:28:10 UTC, Christian wrote:

    An article in the official Go blog suggests to use panic() only internally
    in packages:

    "The convention in the Go libraries is that even when a package uses panic
    internally, its external API still presents explicit error return values."
    http://blog.golang.org/2010/08/defer-panic-and-recover.html

    But there are counterexamples like
    http://golang.org/pkg/regexp/#MustCompile So using panic should be ok,
    even in public functions, if there is no good way to continue work:

    "One possible counterexample is during initialization: if the library
    truly cannot set itself up, it might be reasonable to panic, so to speak."
    http://golang.org/doc/effective_go.html#panic

    My extract from this is: *use panic() for programming errors and
    otherwise error return values.* I've written a simple router that must be
    configured with a kind of regex: router.Register("/<city>/<street>",
    myHandler) If the pattern in the first parameter is wrong, panic() is
    called. Do you agree with this usage of panic?

    Another idea to use panic() is parameter checking like it is common in
    Java (http://code.google.com/p/guava-libraries/wiki/PreconditionsExplained).
    That could reduce the debugging time and lead to more robust code without
    the error handling code in trivial cases. Example:

    func Register(path string, handler Handler) {
    if path == "" {
    panic("path must not be empty")
    }
    ...
    }

    What do you think of this kind of parameter checking?

    panic() takes an empty interface as parameter and is usally called with a
    string. When would be useful to pass something else? And how can the
    developer get a useful error message then?

    Regards
    Christian
    --
  • André Moraes at Nov 22, 2012 at 12:01 pm

    My extract from this is: use panic() for programming errors and otherwise
    error return values. I've written a simple router that must be configured
    with a kind of regex: router.Register("/<city>/<street>", myHandler) If the
    pattern in the first parameter is wrong, panic() is called. Do you agree
    with this usage of panic?

    Another idea to use panic() is parameter checking like it is common in Java
    (http://code.google.com/p/guava-libraries/wiki/PreconditionsExplained). That
    could reduce the debugging time and lead to more robust code without the
    error handling code in trivial cases. Example:
    Don't do things that way, it will hide the possibility of an error,
    the idea behind errors as values instead of exceptions is to force the
    programmer to think when he is coding the use of that function instead
    of simple allowing him to ignore the error handling code.

    If you want a version of a function without the error handling, use
    the MustXXX idiom, that way the user of your library is aware of a
    possible panic (don't forget to document the function to explict
    this).

    How I would do it:

    type Error string
    func (e Error) Error() string {
    return string(e)
    }

    const (
    EmptyRoute = Err("Routes cannot be empty")
    )

    // Register the handler, the path cannot be empty.
    //
    // If the path is empty return EmptyRoute, otherwise return nil
    func Register (path string, handler Handler) error {
    if path == "" {
    return EmptyRoute
    }
    // do processing
    return nil
    }

    // Does the same thing Register does, but panic if an error happens
    func MustRegister(path string, handler Handler) {
    err = Register(path, handler)
    if err != nil { panic(err) }
    }


    // User of your library

    func main() {
    // ...
    // no reason to continue the program if can't register the handlers
    MustRegister("/route1", handler1)
    MustRegister("/route2', handler2)
    // ...
    // here the handlers can have a path defined by the user
    // crazy requiriments...
    if err = Register(userSuppliedRoute, handleUserSpecificThing); err {
    log.Printf("Unable to load user route %v from database", userSuppliedRoute)
    }
    }
    func Register(path string, handler Handler) {
    if path == "" {
    panic("path must not be empty")
    }
    ...
    }
    --
    André Moraes
    http://amoraes.info

    --
  • André Moraes at Nov 22, 2012 at 12:03 pm

    // ...
    // here the handlers can have a path defined by the user
    // crazy requiriments...
    if err = Register(userSuppliedRoute, handleUserSpecificThing); err {
    log.Printf("Unable to load user route %v from database", userSuppliedRoute)
    }
    }
    The if should be:
    if err = Register(userSuppliedRoute, handleUserSpecificThing); err != nil {
    // ... body
    }

    --
    André Moraes
    http://amoraes.info

    --
  • Dustin Sallings at Nov 22, 2012 at 10:53 pm

    Christian writes:

    "One possible counterexample is during initialization: if the library
    truly cannot set itself up, it might be reasonable to panic, so to
    speak."
    http://golang.org/doc/effective_go.html#panic
    In some of my code, I broaden the "initialization" concept to anything
    that only go wrong when the only failure possible is by the programmer
    misusing the API. These things can typically trivially be picked up at
    unit test time (e.g. if your package init doesn't run because of a
    MustCompile, then you'll know it early).

    For example, go-probably allows you to create count-min sketches with
    dimensions you provide based on your requirements. If you try to make a
    sketch that's not at least one element wide or tall, your program
    couldn't possibly work and I just panic there.

    As I implied above, I *would* return an error if the function already
    returned an error. i.e. I only would use panic if the error is easily
    avoidable and the function couldn't fail otherwise.

    You'll see similar usage in math/big's divide functions, for example.
    If you try to divide by zero, it'll panic, because that's the only thing
    you can't divide by, the only reason the function would have to error,
    and you just shouldn't do that.

    --
    dustin

    --

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupgolang-nuts @
categoriesgo
postedNov 22, '12 at 10:28a
activeNov 22, '12 at 10:53p
posts6
users5
websitegolang.org

People

Translate

site design / logo © 2021 Grokbase