control-flujo-if-else-scope

If and else conditionals in Go with variable scope

  • 4 min

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:

  1. No parentheses are used around the condition.
  2. 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")
    }
}
Copied!

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")
    }
}
Copied!

Incorrect (Syntax Error):

if score > 90 {
    ...
}
else { // ERROR: syntax error: unexpected else, expecting }
    ...
}
Copied!

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.
}
Copied!

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
}
Copied!

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 clean
Copied!

Variable scope

Understanding scope is important to avoid shadowing variables and logic bugs.

  1. Variables declared before the if: are visible inside and outside the block.
  2. Variables declared in the initialization: are visible inside the if block and any chained else if and else blocks. 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)
}
Copied!

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.