# cmp.Or Evaluation Semantics vs. Inline If-Else Chains in Go

> Understand cmp.Or evaluation semantics vs. if-else chains in Go. Learn how cmp.Or eagerly evaluates arguments, ensuring side effects always execute.

- Repository: [JetBrains/go-modern-guidelines](https://github.com/jetbrains/go-modern-guidelines)
- Tags: deep-dive
- Published: 2026-09-04

---

**Unlike inline if-else chains, `cmp.Or` evaluates all arguments eagerly before returning the first non-zero value, meaning side-effects in later arguments always execute regardless of earlier results.**

Understanding the distinction between `cmp.Or` evaluation semantics and traditional control flow is critical for writing efficient Go code. The `jetbrains/go-modern-guidelines` repository explicitly documents this behavioral difference, noting that while `cmp.Or` offers conciseness for fallback chains, it fundamentally differs from short-circuiting if-else logic in how it processes arguments.

## Eager vs. Lazy Evaluation: The Core Semantic Difference

The primary distinction lies in when arguments are computed. **Inline if-else chains** use **lazy evaluation** (short-circuiting): they stop evaluating as soon as a non-zero value is found, never touching subsequent branches. In contrast, **`cmp.Or`** uses **eager evaluation**: every argument is computed before the function is called, regardless of which value will ultimately be returned.

As implemented in the `jetbrains/go-modern-guidelines` repository, the [`FEATURES.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md) file explicitly warns that "all arguments are evaluated before the call" (line 966). This means `cmp.Or(a, b, c)` will execute the logic for `a`, `b`, and `c` unconditionally, whereas an if-else equivalent would only evaluate `b` if `a` was zero, and `c` only if both predecessors were zero.

## Side Effects and Performance Implications

Because `cmp.Or` lacks short-circuiting, any function calls or expressions passed as arguments will always run. This creates a significant semantic and performance divergence from if-else chains.

Consider this side-effecting example:

```go
func getEnv() string {
    fmt.Println("Reading env")
    return os.Getenv("CONFIG_PATH")
}

func getDefault() string {
    fmt.Println("Computing default")
    return defaultConfigPath()
}

```

Using an **inline if-else chain** provides lazy evaluation and controlled side effects:

```go
// Inline if-else chain (short-circuiting)
func demoChain() string {
    if v := getEnv(); v != "" {
        return v  // Only "Reading env" is printed
    }
    return getDefault()
}

```

Using **`cmp.Or`** triggers eager evaluation of all arguments:

```go
import "golang.org/x/exp/constraints"

// cmp.Or (eager evaluation)
func demoOr() string {
    // Both "Reading env" AND "Computing default" are printed
    return cmp.Or(getEnv(), getDefault())
}

```

Running `demoChain()` prints only **"Reading env"** (stopping if the environment variable exists), while `demoOr()` prints **both** lines even when the first value is non-empty. For expensive computations or operations with external side effects (logging, database queries, network calls), this eager evaluation can introduce unnecessary overhead.

## Guidelines from the jetbrains/go-modern-guidelines Repository

The repository provides specific guidance on when to apply each pattern. According to [`README.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/README.md) (line 7), `cmp.Or(a, b, c)` is recommended as a concise alternative "instead of a chain of nil checks," emphasizing its utility for simple value fallbacks. However, the [`FEATURES.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md) documentation (line 966) explicitly cautions developers about the eager evaluation semantics to prevent accidental side-effect execution.

The [`guidelines_test.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/guidelines_test.go) file contains validation logic ensuring that `cmp_or` appears correctly in the generated guidelines list, confirming this distinction is treated as a first-class concern within the repository's recommendations.

## When to Use Each Approach

Choose between these patterns based on argument purity and computational cost:

- **`cmp.Or`** – Use for pure values, constants, or cheap computations without side effects. Ideal for selecting the first non-zero string from environment variables, configuration defaults, or hardcoded fallbacks where evaluation order does not matter.

- **Inline if-else chains** – Use when arguments involve expensive operations, I/O, or side effects. The lazy evaluation ensures you only pay for the computation you actually need, and it provides explicit control over when functions execute.

## Summary

- **`cmp.Or` uses eager evaluation**, computing all arguments before the function call, while inline if-else chains use lazy evaluation with short-circuiting.
- **Side effects always occur** with `cmp.Or` arguments, unlike if-else chains where only the taken branch executes.
- **Performance characteristics differ** significantly when arguments involve expensive computations; if-else chains avoid unnecessary work.
- The `jetbrains/go-modern-guidelines` repository endorses `cmp.Or` for concise nil-check chains but explicitly documents its evaluation semantics in [`FEATURES.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md) (line 966).

## Frequently Asked Questions

### What is the main difference between `cmp.Or` and an if-else chain?

`cmp.Or` evaluates all arguments eagerly before the function call, returning the first non-zero value found, whereas inline if-else chains use lazy evaluation, stopping at the first non-zero value and never executing subsequent branches. This semantic difference affects both performance and side-effect behavior.

### Does `cmp.Or` short-circuit like logical OR operators?

No. According to the `jetbrains/go-modern-guidelines` repository's [`FEATURES.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md) (line 966), all arguments are evaluated before the call returns, meaning there is no short-circuiting behavior. This contrasts sharply with the `||` operator or if-else statements, which halt evaluation immediately upon finding a truthy value.

### Can I safely use `cmp.Or` with functions that have side effects?

Only if you intend for all side effects to occur. Because `cmp.Or` evaluates every argument unconditionally, any function calls with side effects will execute regardless of which value is ultimately returned. For operations involving logging, database queries, or state modifications, traditional if-else chains provide the necessary control to avoid unwanted execution.

### When should I use `cmp.Or` over explicit nil checks?

The [`README.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/README.md) (line 7) in the `jetbrains/go-modern-guidelines` repository recommends `cmp.Or` specifically for concise fallback chains when working with pure values or cheap computations. It replaces verbose if-else nil checks with a single expression, but should be avoided when arguments involve costly work or side effects that require conditional execution.