The if statement with an attitude
You are writing a validator for a configuration file. You need to check if the Port field is set. In JavaScript, you write if (config.port) and move on. In Python, if config.port: works the same way. In Go, the compiler stops you immediately. The Port field is an integer. Go does not treat zero as false. You must write if config.Port > 0.
This feels verbose at first. It saves you from a whole class of bugs where a valid zero value gets silently skipped. Go's if statement does three jobs: it branches execution, it enforces boolean discipline, and it creates a temporary scope for variables. Most languages do the first. Go does all three without ceremony.
The if statement in Go has no parentheses around the condition. Braces are mandatory. You can include an initialization statement that runs before the condition and scopes variables to the block. This design keeps code readable and prevents indentation-based bugs.
Syntax and scope
The basic structure is if followed by a condition, then a block in braces. An optional else block follows immediately. The condition must evaluate to a boolean. The compiler rejects any other type.
Here's the simplest form: a condition and two branches.
// CheckStatus prints a message based on the status code.
func CheckStatus(code int) {
// No parentheses around the condition. Braces are mandatory.
if code == 200 {
fmt.Println("OK")
} else {
fmt.Println("Error")
}
}
The compiler enforces braces. There is no single-line shortcut. If you omit the braces, the compiler rejects the program with expected '}', found .... This rule eliminates the dangling-else bug that plagues languages allowing braceless blocks. The structure is always clear.
The else keyword must appear on the same line as the closing brace of the if block. If you put else on a new line, the compiler thinks the if statement ended and complains with expected '}', found 'else'. This rule prevents accidental semicolon insertion errors.
Go does not allow parentheses around the condition. Writing if (x > 0) triggers a syntax error. The parentheses are redundant noise. The language design removes them to keep the signal high.
The initialization statement
The killer feature of Go's if is the initialization statement. You can declare variables and run code right inside the if, before the condition. These variables are scoped to the entire if-else chain. They vanish when the block ends.
This pattern is the standard way to handle errors in Go. You call a function, capture the result and the error, and check the error immediately. The result variable is only available where you need it.
Here's the pattern that makes Go code concise: the initialization statement.
// ProcessFile reads a file and prints its length.
func ProcessFile(path string) {
// Stat returns file info and an error. The variables are scoped to this if block.
if info, err := os.Stat(path); err == nil {
// info is available here because the init statement succeeded.
fmt.Printf("Size: %d\n", info.Size())
} else {
// err is also available in the else block.
fmt.Printf("Error: %v\n", err)
}
// info and err are not available here. The scope ended.
}
The initialization statement runs before the condition. The variables it declares are visible in the if block, the else block, and any else if blocks. This scope rule is intentional. It lets you use the result in the success path and the error in the failure path without leaking variables into the outer function.
If you only need the error and want to discard the result, use the blank identifier. This tells the compiler you intentionally ignored the value.
// DiscardResult calls a function but only cares about the error.
func DiscardResult() {
// _ discards the first return value. err is scoped to the block.
if _, err := doWork(); err != nil {
fmt.Println("Failed")
}
}
The convention in Go is to check errors immediately after the call. You write if err != nil { return err }. This boilerplate is verbose by design. It makes the unhappy path visible. Every function that returns an error must be handled. The compiler does not force you to handle errors, but the community treats unhandled errors as a bug.
Boolean discipline
Go requires explicit boolean conditions. You cannot rely on truthiness. A non-zero integer, a non-empty string, or a non-nil pointer is not automatically true. You must compare the value.
If you write if x { } where x is an integer, the compiler rejects it with non-bool ... used as condition. This rule forces you to decide what "true" means. Is zero valid? Is an empty string valid? The code must say.
This discipline prevents subtle bugs. In a language with truthiness, if config.Port skips the block when the port is 0, which might be a valid default. In Go, if config.Port is a compile error. You have to write if config.Port > 0 or if config.Port != 0. The intent is explicit.
The same rule applies to pointers. A non-nil pointer is not true. You must write if ptr != nil. A non-empty slice is not true. You must write if len(slice) > 0.
Go's boolean discipline makes code self-documenting. The condition tells you exactly what the code considers success. There is no hidden coercion.
Formatting and the compiler
The Go community uses gofmt to format code. You do not argue about indentation or brace placement. The tool decides. Most editors run gofmt on save. This convention ensures consistency across projects.
The if statement formatting is strict. The opening brace goes on the same line as the if. The else goes on the same line as the closing brace. gofmt enforces this. If you write code that violates these rules, the formatter fixes it automatically.
// Correct formatting enforced by gofmt.
if condition {
// body
} else {
// else body
}
You never write if (condition). You never write if condition { with the brace on the next line. The formatter handles it. Trust the tool. Argue logic, not formatting.
The compiler also checks for unused variables. If you declare a variable in the initialization statement but never use it, the compiler rejects the program with declared and not used. This rule catches typos and dead code early. If you need to declare a variable but don't use it yet, use the blank identifier or remove the declaration.
Decision matrix
Go provides multiple ways to branch. Pick the right tool based on the structure of your logic.
Use an if statement when you have a single boolean condition or a small chain of mutually exclusive checks.
Use an if statement with an initialization clause when you need to call a function and use its result only within the conditional block.
Use an else if chain when you have multiple conditions that depend on different variables or complex expressions.
Use a switch statement when you are comparing one value against many constants or types.
Use a switch statement with no expression when you want to group multiple boolean conditions under case labels.
Use plain sequential code when you don't need branching: the simplest thing that works is usually the right thing.