Which is more idiomatic?

Here we have an apparent conflict between two idioms:

  1. The Comma, Ok Idiom with If Statement: limiting the scope of variables to only where they are needed, frequently used for error handling and type assertions.
  2. Avoiding unnecessary else blocks after an if block that ends with a return statement to reduce nesting and improve readability.

Comma, Ok

	if bs, err := json.Marshal(b); err != nil {
		handleError(w, err)
		return
	} else {
		fmt.Fprintln(w, string(bs))
	}

Avoiding unnecessary else

	var bs []byte
	if bs, err = json.Marshal(b); err != nil {
		handleError(w, err)
		return
	} 
	fmt.Fprintln(w, string(bs))

Moving “bs” outside/above the if statement increases its scope and could result in a bug.

Golint sides with idiom #2:

if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)

Notes

  1. Context matters, for smaller functions where the broader scope of a variable does not significantly increase complexity or risk, simplifying control flow by avoiding unnecessary else blocks might be preferred. In more complex functions, or when a variable genuinely should not be accessible beyond a specific block of code, maintaining tighter scope might be prioritized.
  2. golint provides suggestions based on common best practices, but it doesn’t encompass all possible contexts or nuances of code. It’s a tool to aid development, not a strict set of rules that must be followed in every situation.

Is Golint thus the authoritative answer making #2 the more idiomatic choice?

I usually write something like

bs, err := json.Marshal(b)
if err != nil {
	handleError(w, err)
	return
} 
fmt.Fprintln(w, string(bs))
3 Likes

Thanks for replying!

This does seem to be a very popular way of doing it. The benefit is ease and clarity by using the shorthand declaration for ‘bs’ as well, and not having to look up and specify its type (similar to option #1 in my post). The trade-off is that this also increases the scope of ‘err’.

This is plenty idiomatic and if you look at the go source you’ll see it all over the place. For example in encoding/json:

// convertNumber converts the number literal s to a float64 or a Number
// depending on the setting of d.useNumber.
func (d *decodeState) convertNumber(s string) (any, error) {
	if d.useNumber {
		return Number(s), nil
	}
	f, err := strconv.ParseFloat(s, 64)
	if err != nil {
		return nil, &UnmarshalTypeError{Value: "number " + s, Type: reflect.TypeFor[float64](), Offset: int64(d.off)}
	}
	return f, nil
}

Source

well, from my experience reading Go code, this is by far the most common way of doing this kind of stuff.

I use option 1. only if theres no return value of the function except the error

if err := doSomething(); err != nil {
  // handle error
}

and TBH, never seed option 2. used

Thank you!

This is something I didn’t consider. The idiomatic way to do it depends on whether you need to do further processing on the return values of the variables involved in the Comma, Ok idiom.

Thank you! How did I not think about looking for examples in the standard library? Although there are some pretty whimsical examples in there (yes, sort package, I’m looking at you!).