New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: Go 2: add a Causer interface for errors #27020

Closed
darkfeline opened this Issue Aug 16, 2018 · 3 comments

Comments

Projects
None yet
4 participants
@darkfeline

darkfeline commented Aug 16, 2018

This proposal is for adding a new Causer interface for errors to the standard library to address the problem of error propagation through multiple packages. (This idea is inspired by the causer interface in the https://github.com/pkg/errors package.)

Consider three packages A, B, and C, where package A calls a function in package B and that function calls a function in package C. The general problem that this proposal addresses is how should package B propagate an error from package C back to package A.

There are three ways B could handle an error returned from C:

  1. B could return the error directly.
  2. B could return its own error, which exposes the error from package C by:
    a. extracting some information from the error (e.g. fmt.Errorf("blah blah: %s", err))
    b. embedding the original error (e.g. errors.Wrap).

Approach 1 neglects to add context, which could make it hard for package A to interpret the error. Approach 2a includes context from package B, but partially destroys information from the original error. Approach 2b is the most versatile, allowing package B to provide context while preserving the original error.

The problem with approach 2a currently is that there is no standard way to embed an error, and this is really something that benefits from a blessed interface that the Go ecosystem can then agree to use.

The interface is simple:

type Causer {
    Cause() error
}

Example usage:


// Wrap returns a Causer with Cause set to the given error.
func Wrap(err error, msg string) error {}

// Cause recursively tests for Causer and calls Cause and returns the
// original error.
func Cause(e error) error {}

func Frob() error {
	err := RetryTemporaryErrors(doRequest())
	if err != nil {
		orig := Cause(err)
		if orig, ok := orig.(Timeout); ok && orig.Timeout() {
			fmt.Println("Server is down")
		} else {
			fmt.Printf("Something weird happened: %s\n", orig)
		}
	}
	return nil
}

func RetryTemporaryErrors(f func() error) error {
	for {
		err := f()
		if err == nil {
			return nil
		}
		if err, ok := err.(Temporary); !(ok && err.Temporary()) {
			return Wrap(err, "not temporary error")
		}
		err = fiddleThing()
		if err != nil {
			return err
		}
		time.Sleep(10 * time.Second)
	}
}

When I was almost done writing this, I found #25675 which appears to be suggesting something similar. However, this proposal is drastically narrower in scope (just about the Causer interface) and the rationale is that this really needs to be standardized to be useful since it is a problem with how errors cross package boundaries.

@gopherbot gopherbot added this to the Proposal milestone Aug 16, 2018

@gopherbot gopherbot added the Proposal label Aug 16, 2018

@ALTree ALTree changed the title from Proposal: Add a Causer interface for errors to proposal: Add a Causer interface for errors Aug 16, 2018

@ianlancetaylor ianlancetaylor changed the title from proposal: Add a Causer interface for errors to proposal: Go 2: add a Causer interface for errors Aug 16, 2018

@ianlancetaylor ianlancetaylor added the Go2 label Aug 16, 2018

@ianlancetaylor

This comment has been minimized.

Contributor

ianlancetaylor commented Aug 16, 2018

I'm going to mark this as Go 2 because it should be considered in conjunction with other discussions about error handling for Go 2. We should have a coherent overhaul of error handling, not an incremental approach.

CC @neild @jba

@jimmyfrasche

This comment has been minimized.

Member

jimmyfrasche commented Aug 24, 2018

It would probably suffice to add a Cause() error method to all types that wrap another error in the stdlib.

@ianlancetaylor

This comment has been minimized.

Contributor

ianlancetaylor commented Oct 2, 2018

I think this can be subsumed into the general discussion of errors-as-values: https://go.googlesource.com/proposal/+/master/design/go2draft-error-inspection.md . Closing in favor of that general discussion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment