Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rethinking Errors in Go

Rethinking Errors in Go

We're starting to think about Go 2. This talk addresses some of the current issues with handling errors in Go. We introduce a burner package that addresses these issues by introducing a unified model of error, defer, and panic. Although this package can simplify Go code today, we hope that experience with this package can help us understand how error handling can be improved at the language level.

Marcel van Lohuizen

November 06, 2017
Tweet

More Decks by Marcel van Lohuizen

Other Decks in Programming

Transcript

  1. Rethinking Errors in Go
    Marcel van Lohuizen

    Go core team

    View full-size slide

  2. r, err := os.Open(“foo.txt”)
    if err != nil {
    return fmt.Errorf(“oops: %v”, err)
    }
    defer r.Close()

    View full-size slide

  3. Semantics first!
    Why? 

    error handling can be tricky

    View full-size slide

  4. func writeToGS(c net.Context, bkt, dst string, r io.Reader) (err error) {

    w := client.Bucket(bkt).Object(dst).NewWriter(c)
    defer w.CloseWithError(err)
    if _, err = io.Copy(w, r); err != nil {
    return fmt.Errorf(“oops: %v”, err)

    }
    return nil
    }

    View full-size slide

  5. func writeToGS(c net.Context, bkt, dst string, r io.Reader) (err error) {
    err = errPanicking
    w := client.Bucket(bkt).Object(dst).NewWriter(c)
    defer w.CloseWithError(err)
    if _, err = io.Copy(w, r); err != nil {
    return fmt.Errorf(“oops: %v”, err)

    }
    return err
    }
    var errPanicking = errors.New(“panicking”)
    handle panic FAIL

    View full-size slide

  6. func writeToGS(c net.Context, bkt, dst string, r io.Reader) (err error) {

    w := client.Bucket(bkt).Object(dst).NewWriter(c)
    err = errPanicking
    defer func() {
    if err != nil {
    _ = w.CloseWithError(err)
    } else if err = w.Close(); err != nil {
    err = fmt.Errorf(“oh noes: %v”, err)

    }
    }
    if _, err = io.Copy(w, r); err != nil {
    return fmt.Errorf(“oops: %v”, err)

    }
    return err
    }
    return error from Close FAIL

    View full-size slide

  7. func writeToGS(c net.Context, bkt, dst string, r io.Reader) (err error) {
    w := client.Bucket(bkt).Object(dst).NewWriter(c)
    err = errPanicking
    defer func() {
    if err != nil {
    _ = w.CloseWithError(err)
    } else if err = w.Close(); err != nil {
    err = fmt.Errorf(“oh noes: %v”, err)
    }
    }()
    if _, err = io.Copy(w, r); err != nil {
    return fmt.Errorf(“oops: %v”, err)
    }
    return nil
    }
    var errPanicking = errors.New(“panicking”)

    View full-size slide

  8. how to approach simplifying this?
    semantics first!

    View full-size slide

  9. an error is recoverable
    panic is not

    (sort of)

    View full-size slide

  10. there is overlap
    not recognized in Go

    View full-size slide

  11. Burner package

    (github.com/mpvl/errc)

    View full-size slide

  12. funnel all errors,
    including panics,
    to a single error variable

    View full-size slide

  13. func writeToGS(c net.Context, bkt, dst string, r io.Reader) (err error) {
    e := errc.Catch(&err)
    defer e.Handle()

    w := client.Bucket(bkt).Object(dst).NewWriter(c)
    e.Defer(w.CloseWithError, msg(“oh noes”))
    _, err = io.Copy(w, r)
    e.Must(err, msg(“oh no”))
    return nil
    }

    View full-size slide

  14. semantics first pays off!

    View full-size slide

  15. Try it out!
    Feedback Welcome!
    Marcel van Lohuizen, Go core Team
    github.com/mpvl/errc

    github.com/mpvl/errd

    @mpvl_

    View full-size slide