Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use 'comma ok' idiom or return pointer?

Tags:

idioms

go

Consider the following Go snippet:

func sheep() (int, bool) {
    return 1, true
}

func main() {
    if dolly, ok := sheep() {
         //do something
    }
}

As I read on 'Effective Go' this is called the 'comma ok' idiom. As far as I can tell this is used to distinguish from a 'found' and 'not found' thing.

The same can be achieved via:

type Sheep struct {}

func sheep() *Sheep {
    return &Sheep{}
}

func main() {
    if dolly := sheep(); dolly != nil {
         //do something
    }
}

The latter example seems to fulfill the same purpose, perhaps even nicer. With the 'comma ok' example the assignment is only valid in the if block.

Perhaps I'm missing some considerations. Which pattern is preferred? And why?

A brief example: http://play.golang.org/p/ATxvle38iE

like image 428
harm Avatar asked Apr 28 '14 16:04

harm


2 Answers

In Go, a nil value may be a perfectly good value. For example a nil slice works (almost) like an empty slice, and the same may be true for user-defined pointer receivers.

For this reason, the comma-ok or comma-error idiom is usually preferred because it makes it obvious that the caller of the function needs to treat the error case (or the not-ok) case explicitly.

So, these are idiomatic when the Sheep return value may not be valid:

func sheep() (*Sheep, bool) {...}
func sheep() (s *Sheep, ok bool) {...}  // Adding names to make it clearer
func sheep() (*Sheep, error) {...}

And this is idiomatic only when the return value is always valid:

func sheep() *Sheep {...}

This is an area where Go is different from other languages, where a nil return value may be used to signal an error. The Go idioms of comma-ok and comma-error neatly work around the "billion-dollar mistake" of nil pointers by making code that doesn't deal with invalid return values look wrong. If you write idiomatic code, you can immediately see when errors are being ignored: for example the assignment to s2 here immediately jumps out as suspicious:

s1 := sheep()
s2, _ := sheep()
like image 69
Paul Hankin Avatar answered Nov 12 '22 20:11

Paul Hankin


Both are acceptable, plus you missed the most common idiom; Returning value, error.

The "comma ok" idiom as referenced in "Effective Go" is typically reserved for the builtin operations, like reading from a map or channel, and for type assertions.

I would use it if you need to return a value where a pointer would be unnecessary, inconvenient, or where nil is a valid value; but depending on the situation value, error could be just as good.

like image 21
JimB Avatar answered Nov 12 '22 20:11

JimB