The comma, ok Pattern

This lesson briefly discusses the uses of the comma, ok pattern in Go language.

While studying the Go-language, we encountered several times the so-called comma, ok idiom where an expression returns two values: the first of which is a value or nil, and the second is true/false or an error. An if-condition with initialization and then testing on the second-value leads to succinct and elegant code. This is a significant pattern in idiomatic Go-code. Here are all cases summarized:

Testing for errors on function return

var value Type_value
var err error
if value, err = pack1.Func1(param1); err != nil {
  fmt.Printf("Error %s in pack1.Func1 with parameter %v", err.Error(), param1)
  return err
}
// no error in Func1:
Process(value)

Other places where it is used:

os.Open(file), strconv.Atoi(str)

The following code will not compile because the scope of the file variable is the if-block only:

if file, err := os.Open("input.dat"); err != nil {
  fmt.Printf("An error occurred on opening the inputfile\n" +
  "Does the file exist?\n" +
  "Have you got access to it?\n")
  return // exit the function on error
}
defer file.Close()
reader := bufio.NewReader(file)

In this case, declare the variables upfront:

var file *os.File
var err error

and then file, err := os.Open("input.dat") can be replaced by: file, err = os.Open("input.dat").

The function in which this code occurs returns the error to the caller, giving it the value nil when the normal processing was successful and so has the signature:

func SomeFunc() error {
  ...
  if value, err = pack1.Func1(param1); err != nil {
    ...
    return err
  }
  ...
  return nil
}

The same pattern is used when recovering from a panic with defer. A good pattern for clean error checking is using closures.

Testing if a key-value item exists in a map

Does this means that map1 has a value for key1? Look at the following snippet:

if value, isPresent = map1[key1]; isPresent {
  Process(value)
}
// key1 is not present
...

Testing if an interface variable is of certain type

if value, ok := varI.(T); ok {
  Process(value)
}
// varI is not of type T

Testing if a channel is closed

for input := range ch {
  Process(input)
}

or:

var input Type_of_input
var err error
for {
  if input, open = <-ch; !open {
    break // channel is closed
  }
  Process(input)
}

This is how this pattern resolves many issues in a single line. Similarly, in the next lesson, we’ll be studying the defer pattern, used several times before in this course.

Get hands-on with 1200+ tech skills courses.