Skip to content
This repository has been archived by the owner on Dec 1, 2021. It is now read-only.

Proposal: errors.Pop() #84

Open
bobbytables opened this issue Sep 9, 2016 · 5 comments
Open

Proposal: errors.Pop() #84

bobbytables opened this issue Sep 9, 2016 · 5 comments

Comments

@bobbytables
Copy link

Hello, we are wrapping our errors in our grpc service, and we came across a situation where we wanted to return the top of the wrapped errors in the return of our endpoints.

For reference, a typical grpc endpoint signature looks like this:

func (s *Server) Hello(ctx context.Context, req *pb.HelloReq) (*pb.HelloResp, error) {
  // ...
}

The method signature has an error type as the second return value. We've written grpc middleware that receives the return values of this function, and does the proper logging, metrics, etc, before ultimately returning to the requesting client.

In the middleware, we log the entire error chain, but what we'd like to return to the grpc client in the error return param is the top error in the chain. So I'm proposing the ability to do this:

first := errors.New("first")
second := errors.Wrap(first, "second")
third := errors.Wrap(second, "third")

// prints the entire chain:
third.Error() // [ third: second: first ]

// proposed option:
errors.Pop(third) // returns an error type with ONLY "third" and no chain attached

Currently there's no easy (that I know of 😄 ) way to get the top most of an error chain. This would allow us to do so.

Thoughts?

@davecheney
Copy link
Member

Thank you for this suggestion. I understand what you want to implement, but I do not understand why. Can you show me how a caller of Pop would use the result?

@tux-eithel
Copy link

Hi!
I'm also agree with @bobbytables.
In my case a Pop() function is useful to isolate my library errors form others library.
Sometimes I wrap errors inside my function (which returns error), other times no.

var (
    ErrNetwork = errors.New("network erorr")
    ErrDB      = errors.New("database erorr")
)

func main() {

    err := funcWithError()

    switch errors.Pop(err) {
    case ErrNetwork:
        // do stuff
    case ErrDB:
        // do stuff
    default:
        // do other stuff
    }

...
}

@davecheney
Copy link
Member

@tux-eithel thanks for your comment. I don't think your sample code works because the errors returned from this package cannot be compared for equality. This is by design.

@cris-hart
Copy link

@davecheney My example it too much simplified. The Pop() may returns an errors and the switch could be done on after call Error() function.

var (
    ErrNetwork = errors.New("network erorr")
    ErrDB      = errors.New("database erorr")
)

func main() {

    err := funcWithError()

    pop := errors.Pop(err)

    switch pop.Error() {
    case ErrNetwork:
        // do stuff
    case ErrDB:
        // do stuff
    default:
        // some error from other libraries
    }

...
}

@davecheney
Copy link
Member

davecheney commented Oct 29, 2016

You can write errors.Pop yourself

if err, ok := err.(interface{Cause() error}); ok {
      cause := err.Cause()
      ///
}

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants