11

I have recently gotten into Go and have seen a lot of discussion about how to do error handling.

the pattern that I have seen laid out is the following:

err := DoSomething() if err != nil { //handle } // continue 

often times when managing amqp connections, my condition is that I want to continue only if the error is nil, because then I need to do something on the connection:

c, err := Connect() if err != nil { return nil, err } s,err := c.RegisterSomethingOnConnection() if err != nil { return nil, err } val, err := s.DoSomething() return val, err 

as you can see I only want to run the line c.RegisterSomethingOnConnection if the error returned from Connect() is nil.

However, I dislike the above due to the early returns. Early returns make me uncomfortable because in the long run it hurts readability and obscures exactly when a function exits. My solution so far has been to do the following:

var err error var val ReturnType c,err := Connect() if err == nil { s,err := c.RegisterSomethingOnConnection() if err == nil { val,err = s.DoSomething() } } return val,err 

I like to do this for 2 reasons. First, it prevents returning nil. Second, I find it makes the code more maintainable as you can add easily add functionality before returning (i.e. logging) and not have certain paths miss the added functionality due to an early return.

Is what I have done acceptable idiomatic Go or do I just need to get over my dislike of early returns and follow the that pattern?

1

4 Answers 4

8

One of the Go Prover is:

Don’t just check errors, handle them gracefully

I recommend you to read this post from Dave Cheney

I put the highlights here:

"There is no single way to handle errors. Instead, I believe Go’s error handling can be classified into the three core strategies"

  • Sentinel errors:

if err == ErrSomething { … }

"Using sentinel values is the least flexible error handling strategy, as the caller must compare the result to predeclared value using the equality operator. This presents a problem when you want to provide more context, as returning a different error would will break the equality check."

  • Error types

if err, ok := err.(SomeType); ok { … }

"An error type is a type that you create that implements the error interface. "

  • Opaque errors

x, err := bar.Foo() if err != nil { return err } // use x

"I call this style opaque error handling, because while you know an error occurred, you don’t have the ability to see inside the error. As the caller, all you know about the result of the operation is that it worked, or it didn’t."

.... read all the post.

I think the important aspect of error handling is to Don’t just check errors, handle them gracefully, I hope this can help you.

Sign up to request clarification or add additional context in comments.

3 Comments

thank you, this article was very helpful. If I'm understanding it correctly, it is okay to return an error if you have wrapped it with additional context and only logged it in one place, correct?
@Fuzzerker I think is correct, the errors are for humans and not for code.
@Fuzzerker I like this way, return fmt.Errorf("authenticate failed: %v", err) Don't miss where the error occurs.
0

Go. Best practice to handle error from multiple abstract level:

You should either handle an error, or not handle it but delegate it to a higher level (to the caller). Handling the error and returning it is bad practice as if the caller also does the same, the error might get handled several times.

And see:
Errors are Values by Rob Pike.

Go Error Handling Techniques


You may simplify it:

if err := datastore.Get(c, key, record); err != nil { 

Simplifying repetitive error handling:

In Go, error handling is important. The language's design and conventions encourage you to explicitly check for errors where they occur (as distinct from the convention in other languages of throwing exceptions and sometimes catching them). In some cases this makes Go code verbose, but fortunately there are some techniques you can use to minimize repetitive error handling.

Consider an App Engine application with an HTTP handler that retrieves a record from the datastore and formats it with a template.

func init() { http.HandleFunc("/view", viewRecord) } func viewRecord(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil) record := new(Record) if err := datastore.Get(c, key, record); err != nil { http.Error(w, err.Error(), 500) return } if err := viewTemplate.Execute(w, record); err != nil { http.Error(w, err.Error(), 500) } } 

See: https://blog.golang.org/error-handling-and-go

Comments

0

put some sugar will more sweetly.

import ( "github.com/lingdor/spannerlib/E" ) year := E.Must1(strconv.Atoi(c.Query("year"))) 

is more graceful than :

year,yearErr:=strconv.Atoi(c.Query("year")) if yearErr!=nil { panic(yearErr) } 

1 Comment

There is no place for panic in regular code, which is 99% of what developers write.
0

The other responses don't really answer the actual question.

Is what I have done acceptable idiomatic Go [...]?

Firstly, the following code will not work as intended

err := fn1() if err == nil { err := fn2() // this is a new variable, not the same as the outer one } return err 

The short variable declaration := is redeclaring err inside the inner if block. The err from the outer scope is getting masked by the inner. The code always returns the outer scope err of fn1. If fn2 happened to error, you'll still return nil from fn1.

See this go sample

To fix, just make sure you don't use := in the inner blocks; e.g. in your example we'd instead do something like:

var s SType s,err = c.RegisterSomethingOnConnection() 

I haven't seen this pattern used really. I've used it personally on occasion when I want to run some fallthrough logic for any error. But seems the more idiomatic Go pattern is to return each error individually, despite how much verbose the code is. This could help...

  • avoid the subtle bugs in accidentally redeclaring err
  • get you into the habit of handling each error individually and specially, whereas the nested pattern encourages forwarding errors without handling

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.