An if conditional in Go is a structure that executes code only if a condition is met. It is the most direct way to make a program make decisions.
Any program that does something useful needs to make decisions. “If this happens, do that; if not, do the other thing.”
Let’s see how Go handles conditional statements. If you come from C, Java, or C#, the syntax will look familiar, although there are differences in parentheses and variable scope.
The basic if statement
The basic structure is almost identical to most C-derived languages, but with two golden rules:
- No parentheses are used around the condition.
- Braces
{ }are MANDATORY, even if there is only one line of code inside.
package main
import "fmt"
func main() {
age := 18
// Notice: no parentheses in the condition
if age >= 18 {
fmt.Println("You are an adult")
}
}If you use parentheses if (age >= 18), the compiler won’t complain, but the official formatting tool gofmt will remove them automatically when saving. Go wants a unique and clean style.
else and else if
We can chain conditions logically. There is a strict formatting rule here: the else keyword must be on the same line as the closing brace } of the previous block.
If you put else on a new line, Go (which automatically inserts semicolons at the end of lines) will think the if has finished and give you a compilation error.
func main() {
score := 75
if score >= 90 {
fmt.Println("Excellent")
} else if score >= 70 { // Correct: else on the same line as }
fmt.Println("Good")
} else {
fmt.Println("Passing")
}
}Incorrect (Syntax Error):
if score > 90 {
...
}
else { // ERROR: syntax error: unexpected else, expecting }
...
}Declaring variables inside the if
An interesting difference from C# or Java is that the if in Go can accept a short initialization statement before the condition, separated by a semicolon ;.
Syntax:
if variable := value; condition { ... }
This is incredibly useful for executing a function, saving its result, checking it, and limiting the lifetime of that variable to only the conditional block.
Classic example
Imagine we want to get a value from a function and check if it is valid.
Traditional way (Variable “pollutes” the outer scope):
func main() {
user := getUser() // The 'user' variable lives throughout main
if user == "Luis" {
fmt.Println("Access granted")
}
// Here 'user' is still available, even though we no longer need it.
}Idiomatic Go way (Limited scope):
func main() {
// We define 'user' INSIDE the header of the if
if user := getUser(); user == "Luis" {
fmt.Println("Access granted to", user)
} else {
// 'user' is also accessible in the else block
fmt.Println("Access denied to", user)
}
// Here 'user' NO LONGER EXISTS.
// fmt.Println(user) // This would cause a compilation error
}Why is this so important?
This feature appears constantly in error handling in Go (which we will see later). A very common pattern is:
// We try to do something that might fail
if err := doSomething(); err != nil {
// If there was an error, we handle it here
log.Fatal(err)
}
// Here 'err' no longer exists, the scope is cleanVariable scope
Understanding scope is important to avoid shadowing variables and logic bugs.
- Variables declared before the
if: are visible inside and outside the block. - Variables declared in the initialization: are visible inside the
ifblock and any chainedelse ifandelseblocks. They do not exist outside.
package main
import "fmt"
func main() {
x := 10 // Visible throughout main
if x > 5 {
y := 5 // Local variable of the if block
fmt.Println(x, y) // Works
}
// fmt.Println(y) // ERROR: undefined: y
// Example of shadowing (BE CAREFUL)
if x := 100; x > 10 {
fmt.Println(x) // Prints 100 (the local x of the if shadows the outer x)
}
fmt.Println(x) // Prints 10 (the outer x was not modified)
}Be careful when using := if your intention is to modify an external variable. If you use := inside the if with the same name, you will create a new local variable instead of updating the outer one.