Skip to content

Commit 34f1ad9

Browse files
committed
docs(go): add switch vs if control-flow guidance
- Expand 210-go Simplicity section with discrete outcomes and examples - Cross-link from go-rust-systems skill and go-idioms reference
1 parent be88896 commit 34f1ad9

3 files changed

Lines changed: 54 additions & 0 deletions

File tree

rules/210-go.mdc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,37 @@ func KnownAccounts() map[string]Account {
398398
- **Make the Zero Value Useful**: Types should work correctly when zero-initialized
399399
- **A Little Copying is Better Than a Little Dependency**: Prefer copying small, stable code over adding dependencies
400400

401+
### Switch vs if / else (control flow)
402+
403+
- **Prefer `switch`** when branching on **one** expression with **several discrete outcomes** (for example `r.Method`, CLI subcommand name, enum-like string or int) or when using a **type switch** (`switch v := x.(type)`).
404+
- **Prefer `if`** for **`if err != nil`** and error shaping (`errors.Is` / `errors.As`), **early returns**, **boolean** guards, **two** clear branches, or when conditions are **not** the same discriminant.
405+
- **`switch {` … `case cond:`** is fine when a flat list of conditions reads better than a long `else if` ladder; pick whichever is **clearer**.
406+
- **Do not** force `switch` where a **method on an interface** or a **small registry** (for example map of handlers) is the simpler extension point.
407+
408+
```go
409+
// BAD: Same discriminant repeated in else-if chain
410+
func handleVerb(verb string) error {
411+
if verb == "GET" {
412+
return nil
413+
} else if verb == "POST" {
414+
return nil
415+
} else if verb == "DELETE" {
416+
return nil
417+
}
418+
return fmt.Errorf("unknown verb")
419+
}
420+
421+
// GOOD: switch on one expression
422+
func handleVerb(verb string) error {
423+
switch verb {
424+
case "GET", "POST", "DELETE":
425+
return nil
426+
default:
427+
return fmt.Errorf("unknown verb")
428+
}
429+
}
430+
```
431+
401432
### Dependency Management
402433

403434
**Proverb**: "A little copying is better than a little dependency"

skills/go-rust-systems/SKILL.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ func TestAdd(t *testing.T) {
7878
}
7979
```
8080

81+
### Control flow: `switch` vs `if` / `else`
82+
83+
Prefer **`switch`** for multi-way dispatch on one expression (and type switches); keep **`if`** for errors, booleans, guards, and two-branch logic. Do not rewrite `errors.Is` / `errors.As` chains just to use `switch`. Detail: `rules/210-go.mdc` (Simplicity & Idiomatic Go) and `references/go-idioms.md`.
84+
8185
### Mandatory Hardening Add-On (Go)
8286

8387
For HTTP/API client code, always apply the hardening checks from

skills/go-rust-systems/references/go-idioms.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,25 @@ func FindItem(items []Item, predicate func(Item) bool) *Item {
164164
}
165165
```
166166

167+
## Switch vs if / else
168+
169+
**When to use `switch`**
170+
171+
- One expression, many discrete values (HTTP method, command name, format string, enum-like int).
172+
- Dynamic types on an interface: `switch v := x.(type)`.
173+
174+
**When to keep `if`**
175+
176+
- Error handling (`if err != nil`, `errors.Is`, `errors.As`).
177+
- Two branches, boolean guards, early returns, or unrelated predicates.
178+
179+
**Other**
180+
181+
- `switch {` with `case cond:` is idiomatic when it reads cleaner than `else if`; choose clarity.
182+
- Prefer methods on interfaces or a small handler map when that scales better than a growing `switch`.
183+
184+
See [Effective Go — Switch](https://go.dev/doc/effective_go#switch) and `rules/210-go.mdc` (Simplicity & Idiomatic Go).
185+
167186
## Avoid interface{} When Possible
168187

169188
**Proverb**: "interface{} says nothing"

0 commit comments

Comments
 (0)