Go Conditional Statements
Conditional statements are fundamental to programming logic, allowing you to make decisions and control the flow of your program based on different conditions. Go provides a clean and powerful set of conditional constructs that enable you to write expressive and maintainable decision-making code. This comprehensive guide will teach you everything you need to know about Go's conditional statements, from basic if/else constructs to complex nested conditions and advanced conditional patterns.
Understanding Conditional Statements in Go
What Are Conditional Statements?
Conditional statements allow your program to make decisions and execute different code paths based on whether certain conditions are true or false. They form the backbone of program logic, enabling you to:
- Make decisions based on user input or data values
- Handle different scenarios in your application
- Validate data and respond accordingly
- Control program flow based on runtime conditions
- Implement business logic with complex decision trees
Go's Approach to Conditional Statements
Go's conditional statements are designed with simplicity and clarity in mind:
Clean Syntax
Go's if/else
statements have a clean, readable syntax that eliminates unnecessary parentheses while maintaining clarity.
Boolean Expressions
All conditions in Go must evaluate to boolean values, ensuring type safety and preventing common programming errors.
Consistent Structure
Go's conditional statements follow consistent patterns, making the language predictable and easy to learn.
Early Returns
Go encourages the use of early returns to reduce nesting and improve code readability.
Basic if Statements
Simple if Statement
The most basic conditional statement in Go is the simple if
statement, which executes code only when a condition is true.
package main
import "fmt"
func main() {
// Basic if statement
temperature := 25
if temperature > 20 {
fmt.Println("It's a warm day!")
}
// Another example
age := 18
if age >= 18 {
fmt.Println("You are eligible to vote.")
}
// Boolean variable condition
isRaining := false
if isRaining {
fmt.Println("Don't forget your umbrella!")
} else {
fmt.Println("It's a sunny day!")
}
}
if with Variable Declaration
Go allows you to declare variables directly in the if
statement, which is particularly useful for function calls that return values and errors.
package main
import (
"fmt"
"strconv"
)
func main() {
// Declaring variables in if statement
userInput := "42"
if num, err := strconv.Atoi(userInput); err == nil {
fmt.Printf("Successfully converted '%s' to integer: %d\n", userInput, num)
} else {
fmt.Printf("Failed to convert '%s': %v\n", userInput, err)
}
// Another example with multiple conditions
if x, y := 10, 20; x < y {
fmt.Printf("%d is less than %d\n", x, y)
}
// Using the variable after the if block (if declared in the same scope)
if result := calculateValue(); result > 0 {
fmt.Printf("Calculation successful: %d\n", result)
}
// Note: 'result' is not accessible here unless declared in outer scope
}
func calculateValue() int {
return 42
}
if with Multiple Conditions
Go supports complex boolean expressions using logical operators to combine multiple conditions.
package main
import "fmt"
func main() {
// Multiple conditions with logical operators
age := 25
hasLicense := true
hasInsurance := false
// AND operator (&&)
if age >= 18 && hasLicense {
fmt.Println("You can drive!")
}
// OR operator (||)
if hasLicense || hasInsurance {
fmt.Println("You have some form of coverage.")
}
// Complex boolean expression
if age >= 18 && hasLicense && hasInsurance {
fmt.Println("You can drive legally!")
} else if age >= 18 && hasLicense {
fmt.Println("You can drive, but you need insurance.")
} else if age < 18 {
fmt.Println("You're too young to drive.")
} else {
fmt.Println("You need a license to drive.")
}
// Nested conditions
if age >= 18 {
if hasLicense {
if hasInsurance {
fmt.Println("Full driving privileges!")
} else {
fmt.Println("License but no insurance.")
}
} else {
fmt.Println("No license.")
}
} else {
fmt.Println("Too young.")
}
}
if/else Statements
Basic if/else Structure
The if/else
statement allows you to execute different code blocks based on whether a condition is true or false.
package main
import "fmt"
func main() {
// Basic if/else structure
score := 85
if score >= 90 {
fmt.Println("Grade: A")
} else {
fmt.Println("Grade: Not A")
}
// Temperature example
temperature := 15
if temperature > 25 {
fmt.Println("It's hot outside!")
} else {
fmt.Println("It's not hot outside.")
}
// Boolean condition
isWeekend := true
if isWeekend {
fmt.Println("Time to relax!")
} else {
fmt.Println("Back to work!")
}
}
Multiple else if Conditions
Go supports multiple else if
conditions to handle multiple scenarios in a single conditional chain.
package main
import "fmt"
func main() {
// Multiple else if conditions
score := 85
if score >= 90 {
fmt.Println("Grade: A")
} else if score >= 80 {
fmt.Println("Grade: B")
} else if score >= 70 {
fmt.Println("Grade: C")
} else if score >= 60 {
fmt.Println("Grade: D")
} else {
fmt.Println("Grade: F")
}
// Temperature ranges
temperature := 22
if temperature > 30 {
fmt.Println("It's very hot!")
} else if temperature > 25 {
fmt.Println("It's hot!")
} else if temperature > 20 {
fmt.Println("It's warm!")
} else if temperature > 10 {
fmt.Println("It's cool!")
} else if temperature > 0 {
fmt.Println("It's cold!")
} else {
fmt.Println("It's freezing!")
}
// User role checking
userRole := "admin"
if userRole == "admin" {
fmt.Println("Full access granted.")
} else if userRole == "moderator" {
fmt.Println("Moderate access granted.")
} else if userRole == "user" {
fmt.Println("Basic access granted.")
} else {
fmt.Println("No access granted.")
}
}
Complex Conditional Logic
Go allows you to create sophisticated conditional logic by combining multiple conditions and using parentheses for clarity.
package main
import "fmt"
func main() {
// Complex conditional logic
age := 25
hasLicense := true
hasInsurance := true
isWeekend := false
// Complex boolean expression with parentheses
if (age >= 18 && hasLicense && hasInsurance) || (age >= 21 && isWeekend) {
fmt.Println("You can drive!")
}
// Multiple conditions with different logical operators
userType := "premium"
accountBalance := 1500.0
isActive := true
if userType == "premium" && accountBalance > 1000 && isActive {
fmt.Println("Premium user with sufficient balance and active account.")
} else if userType == "standard" && accountBalance > 500 && isActive {
fmt.Println("Standard user with sufficient balance and active account.")
} else if !isActive {
fmt.Println("Account is inactive.")
} else {
fmt.Println("Insufficient balance or invalid user type.")
}
// Nested conditions for complex logic
weather := "sunny"
temperature := 28
windSpeed := 5
if weather == "sunny" {
if temperature > 25 {
if windSpeed < 10 {
fmt.Println("Perfect day for outdoor activities!")
} else {
fmt.Println("Sunny but windy day.")
}
} else {
fmt.Println("Sunny but cool day.")
}
} else if weather == "rainy" {
fmt.Println("Stay indoors!")
} else {
fmt.Println("Check weather conditions.")
}
}
Advanced Conditional Patterns
Early Return Pattern
Go encourages the use of early returns to reduce nesting and improve code readability. This pattern is particularly useful for error handling and validation.
package main
import (
"fmt"
"strconv"
)
func validateUserInput(input string) error {
// Early return for empty input
if input == "" {
return fmt.Errorf("input cannot be empty")
}
// Early return for invalid length
if len(input) < 3 {
return fmt.Errorf("input must be at least 3 characters long")
}
// Early return for invalid characters
if _, err := strconv.Atoi(input); err != nil {
return fmt.Errorf("input must contain only numbers")
}
// Early return for invalid range
if num, _ := strconv.Atoi(input); num < 1 || num > 100 {
return fmt.Errorf("input must be between 1 and 100")
}
// If we reach here, input is valid
fmt.Printf("Valid input: %s\n", input)
return nil
}
func processUser(userID string, age int, isActive bool) {
// Early return for invalid user ID
if userID == "" {
fmt.Println("Error: User ID is required")
return
}
// Early return for invalid age
if age < 0 || age > 150 {
fmt.Println("Error: Invalid age")
return
}
// Early return for inactive user
if !isActive {
fmt.Println("User account is inactive")
return
}
// Process active user
fmt.Printf("Processing user %s (age: %d)\n", userID, age)
}
func main() {
// Test early return pattern
fmt.Println("Testing early return pattern:")
// Test validation function
testInputs := []string{"", "ab", "abc", "123", "150", "200"}
for _, input := range testInputs {
if err := validateUserInput(input); err != nil {
fmt.Printf("Validation error for '%s': %v\n", input, err)
}
}
fmt.Println()
// Test user processing
processUser("", 25, true) // Empty user ID
processUser("user1", -5, true) // Invalid age
processUser("user2", 25, false) // Inactive user
processUser("user3", 25, true) // Valid user
}
Guard Clauses
Guard clauses are a specific type of early return that checks for error conditions or invalid states at the beginning of a function.
package main
import (
"fmt"
"strings"
)
func processEmail(email string) {
// Guard clause: Check if email is empty
if email == "" {
fmt.Println("Error: Email address is required")
return
}
// Guard clause: Check if email contains @ symbol
if !strings.Contains(email, "@") {
fmt.Println("Error: Email must contain @ symbol")
return
}
// Guard clause: Check if email is too long
if len(email) > 254 {
fmt.Println("Error: Email address is too long")
return
}
// Guard clause: Check if email starts with @
if strings.HasPrefix(email, "@") {
fmt.Println("Error: Email cannot start with @")
return
}
// Guard clause: Check if email ends with @
if strings.HasSuffix(email, "@") {
fmt.Println("Error: Email cannot end with @")
return
}
// If all guard clauses pass, process the email
fmt.Printf("Processing email: %s\n", email)
}
func calculateDiscount(price float64, userType string, isMember bool) float64 {
// Guard clause: Check for invalid price
if price <= 0 {
fmt.Println("Error: Price must be positive")
return 0
}
// Guard clause: Check for invalid user type
if userType != "regular" && userType != "premium" && userType != "vip" {
fmt.Println("Error: Invalid user type")
return 0
}
// Calculate discount based on user type and membership
var discount float64
if userType == "vip" {
discount = 0.25 // 25% discount
} else if userType == "premium" {
discount = 0.15 // 15% discount
} else if isMember {
discount = 0.10 // 10% discount for members
} else {
discount = 0.05 // 5% discount for regular users
}
return price * discount
}
func main() {
fmt.Println("Testing guard clauses:")
// Test email processing
testEmails := []string{
"",
"invalid-email",
"user@",
"@domain.com",
"[email protected]",
}
for _, email := range testEmails {
processEmail(email)
}
fmt.Println()
// Test discount calculation
testCases := []struct {
price float64
userType string
isMember bool
}{
{100.0, "vip", false},
{100.0, "premium", false},
{100.0, "regular", true},
{100.0, "regular", false},
{-50.0, "regular", false}, // Invalid price
{100.0, "invalid", false}, // Invalid user type
}
for _, tc := range testCases {
discount := calculateDiscount(tc.price, tc.userType, tc.isMember)
if discount > 0 {
fmt.Printf("Price: $%.2f, User: %s, Member: %t, Discount: $%.2f\n",
tc.price, tc.userType, tc.isMember, discount)
}
}
}
Conditional Assignment
Go allows you to use conditional statements for assignment, creating concise and expressive code.
package main
import "fmt"
func main() {
// Conditional assignment
score := 85
var grade string
// Traditional approach
if score >= 90 {
grade = "A"
} else if score >= 80 {
grade = "B"
} else if score >= 70 {
grade = "C"
} else if score >= 60 {
grade = "D"
} else {
grade = "F"
}
fmt.Printf("Score: %d, Grade: %s\n", score, grade)
// Conditional assignment with function calls
temperature := 25
var weatherMessage string
if temperature > 30 {
weatherMessage = "It's very hot today!"
} else if temperature > 20 {
weatherMessage = "It's a pleasant day."
} else if temperature > 10 {
weatherMessage = "It's a bit cool."
} else {
weatherMessage = "It's quite cold."
}
fmt.Println(weatherMessage)
// Conditional assignment with complex logic
userAge := 25
hasLicense := true
hasInsurance := true
var drivingStatus string
if userAge < 16 {
drivingStatus = "Too young to drive"
} else if userAge < 18 {
drivingStatus = "Can drive with supervision"
} else if hasLicense && hasInsurance {
drivingStatus = "Full driving privileges"
} else if hasLicense {
drivingStatus = "Can drive but needs insurance"
} else {
drivingStatus = "Needs a license to drive"
}
fmt.Printf("Age: %d, Status: %s\n", userAge, drivingStatus)
}
Boolean Logic in Conditions
Logical Operators in Conditions
Go provides three logical operators that can be used in conditional statements: &&
(AND), ||
(OR), and !
(NOT).
package main
import "fmt"
func main() {
// Logical AND operator (&&)
age := 25
hasLicense := true
hasInsurance := true
if age >= 18 && hasLicense && hasInsurance {
fmt.Println("You can drive legally!")
}
// Logical OR operator (||)
isWeekend := false
isHoliday := true
if isWeekend || isHoliday {
fmt.Println("It's a day off!")
}
// Logical NOT operator (!)
isRaining := false
if !isRaining {
fmt.Println("It's not raining, you can go outside!")
}
// Complex boolean expressions
temperature := 22
isSunny := true
windSpeed := 5
if isSunny && temperature > 20 && windSpeed < 10 {
fmt.Println("Perfect weather for outdoor activities!")
}
// Combining all logical operators
userAge := 25
isStudent := false
hasDiscount := true
if (userAge < 26 && isStudent) || hasDiscount {
fmt.Println("You qualify for a discount!")
}
// Short-circuit evaluation
fmt.Println("Testing short-circuit evaluation:")
// Function that prints and returns a value
checkCondition := func(name string, value bool) bool {
fmt.Printf(" Checking %s: %t\n", name, value)
return value
}
// AND operator with short-circuit
if checkCondition("A", true) && checkCondition("B", false) {
fmt.Println("Both conditions are true")
} else {
fmt.Println("At least one condition is false")
}
// OR operator with short-circuit
if checkCondition("C", true) || checkCondition("D", true) {
fmt.Println("At least one condition is true")
}
}
Boolean Variable Usage
Using boolean variables can make your conditional statements more readable and maintainable.
package main
import "fmt"
func main() {
// Using boolean variables for clarity
age := 25
hasLicense := true
hasInsurance := true
isWeekend := false
// Create meaningful boolean variables
canDrive := age >= 18 && hasLicense && hasInsurance
isDayOff := isWeekend
if canDrive && isDayOff {
fmt.Println("Perfect day for a road trip!")
} else if canDrive {
fmt.Println("You can drive to work.")
} else if isDayOff {
fmt.Println("Enjoy your day off!")
} else {
fmt.Println("Stay home and relax.")
}
// Complex business logic with boolean variables
userType := "premium"
accountBalance := 1500.0
isActive := true
hasValidPayment := true
// Create descriptive boolean variables
isPremiumUser := userType == "premium"
hasSufficientBalance := accountBalance > 1000
canMakePurchase := isActive && hasValidPayment
qualifiesForDiscount := isPremiumUser && hasSufficientBalance
if qualifiesForDiscount && canMakePurchase {
fmt.Println("Premium user with discount can make purchase!")
} else if canMakePurchase {
fmt.Println("Regular user can make purchase.")
} else if !isActive {
fmt.Println("Account is inactive.")
} else if !hasValidPayment {
fmt.Println("Payment method is invalid.")
} else {
fmt.Println("Cannot make purchase.")
}
}
Best Practices for Conditional Statements
Writing Clear and Readable Conditions
package main
import "fmt"
func main() {
// Best practices for writing clear conditions
// 1. Use descriptive variable names
userAge := 25
hasValidLicense := true
hasActiveInsurance := true
// Good: Clear and descriptive
if userAge >= 18 && hasValidLicense && hasActiveInsurance {
fmt.Println("User can drive legally.")
}
// Bad: Unclear variable names
// if a >= 18 && b && c {
// fmt.Println("User can drive legally.")
// }
// 2. Use parentheses for complex expressions
temperature := 25
isSunny := true
windSpeed := 5
humidity := 60
// Good: Clear precedence with parentheses
if (isSunny && temperature > 20) && (windSpeed < 10 || humidity < 70) {
fmt.Println("Good weather for outdoor activities!")
}
// 3. Break complex conditions into multiple statements
score := 85
attendance := 95
hasCompletedProjects := true
// Good: Break into multiple conditions
hasGoodScore := score >= 80
hasGoodAttendance := attendance >= 90
isProjectComplete := hasCompletedProjects
if hasGoodScore && hasGoodAttendance && isProjectComplete {
fmt.Println("Student qualifies for graduation!")
}
// 4. Use early returns to reduce nesting
processUserData := func(userID string, age int, isActive bool) {
// Early return for invalid user ID
if userID == "" {
fmt.Println("Error: User ID is required")
return
}
// Early return for invalid age
if age < 0 || age > 150 {
fmt.Println("Error: Invalid age")
return
}
// Early return for inactive user
if !isActive {
fmt.Println("User account is inactive")
return
}
// Process valid user
fmt.Printf("Processing user %s (age: %d)\n", userID, age)
}
// Test the function
processUserData("", 25, true) // Empty user ID
processUserData("user1", -5, true) // Invalid age
processUserData("user2", 25, false) // Inactive user
processUserData("user3", 25, true) // Valid user
}
Performance Considerations
package main
import (
"fmt"
"time"
)
// Expensive function that takes time to execute
func expensiveOperation() bool {
time.Sleep(100 * time.Millisecond) // Simulate expensive operation
return true
}
// Fast function that executes quickly
func fastOperation() bool {
return false
}
func main() {
fmt.Println("Performance considerations in conditional statements:")
// 1. Order conditions by likelihood (most likely first)
userType := "regular"
isWeekend := false
hasDiscount := true
// Good: Check most likely condition first
if userType == "regular" {
fmt.Println("Regular user processing")
} else if userType == "premium" {
fmt.Println("Premium user processing")
} else if userType == "vip" {
fmt.Println("VIP user processing")
}
// 2. Use short-circuit evaluation to your advantage
// Good: Fast condition first, expensive condition second
if fastOperation() && expensiveOperation() {
fmt.Println("Both operations succeeded")
} else {
fmt.Println("Fast operation failed, expensive operation not executed")
}
// 3. Cache expensive computations
temperature := 25
isSunny := true
windSpeed := 5
// Good: Cache expensive computation
weatherScore := calculateWeatherScore(temperature, isSunny, windSpeed)
if weatherScore > 80 {
fmt.Println("Excellent weather!")
} else if weatherScore > 60 {
fmt.Println("Good weather!")
} else if weatherScore > 40 {
fmt.Println("Fair weather!")
} else {
fmt.Println("Poor weather!")
}
}
func calculateWeatherScore(temp int, sunny bool, wind int) int {
// Simulate expensive weather calculation
time.Sleep(50 * time.Millisecond)
score := temp * 2
if sunny {
score += 20
}
if wind < 10 {
score += 10
}
return score
}
Error Handling Patterns
package main
import (
"fmt"
"strconv"
)
func processUserInput(input string) error {
// Validate input using conditional statements
if input == "" {
return fmt.Errorf("input cannot be empty")
}
if len(input) < 3 {
return fmt.Errorf("input must be at least 3 characters")
}
if len(input) > 50 {
return fmt.Errorf("input must be less than 50 characters")
}
// Check if input is numeric
if _, err := strconv.Atoi(input); err != nil {
return fmt.Errorf("input must be a valid number")
}
// Check if number is in valid range
if num, _ := strconv.Atoi(input); num < 1 || num > 100 {
return fmt.Errorf("input must be between 1 and 100")
}
fmt.Printf("Valid input: %s\n", input)
return nil
}
func authenticateUser(username, password string) (bool, error) {
// Input validation
if username == "" {
return false, fmt.Errorf("username is required")
}
if password == "" {
return false, fmt.Errorf("password is required")
}
if len(username) < 3 {
return false, fmt.Errorf("username must be at least 3 characters")
}
if len(password) < 8 {
return false, fmt.Errorf("password must be at least 8 characters")
}
// Simulate authentication check
if username == "admin" && password == "password123" {
return true, nil
}
return false, fmt.Errorf("invalid username or password")
}
func main() {
fmt.Println("Error handling patterns with conditional statements:")
// Test input processing
testInputs := []string{
"",
"ab",
"abc",
"123",
"150",
"200",
"not-a-number",
}
for _, input := range testInputs {
if err := processUserInput(input); err != nil {
fmt.Printf("Error processing '%s': %v\n", input, err)
}
}
fmt.Println()
// Test authentication
testCredentials := []struct {
username string
password string
}{
{"", "password123"}, // Empty username
{"admin", ""}, // Empty password
{"ad", "password123"}, // Short username
{"admin", "123"}, // Short password
{"user", "password123"}, // Wrong username
{"admin", "wrongpassword"}, // Wrong password
{"admin", "password123"}, // Correct credentials
}
for _, creds := range testCredentials {
success, err := authenticateUser(creds.username, creds.password)
if err != nil {
fmt.Printf("Authentication failed for '%s': %v\n", creds.username, err)
} else if success {
fmt.Printf("Authentication successful for '%s'\n", creds.username)
}
}
}
What You've Learned
Congratulations! You now have a comprehensive understanding of Go's conditional statements:
Basic Conditional Structures
- Simple
if
statements for basic decision making if/else
statements for binary choices- Multiple
else if
conditions for complex decision trees - Variable declaration within conditional statements
Advanced Conditional Patterns
- Early return patterns for cleaner code
- Guard clauses for input validation
- Conditional assignment for concise code
- Complex boolean expressions with logical operators
Boolean Logic and Operators
- Logical AND (
&&
) operator usage - Logical OR (
||
) operator usage - Logical NOT (
!
) operator usage - Short-circuit evaluation behavior
Best Practices and Patterns
- Writing clear and readable conditions
- Performance considerations for conditional statements
- Error handling patterns using conditionals
- Using boolean variables for code clarity
Real-World Applications
- Input validation and error handling
- Business logic implementation
- User authentication and authorization
- Data processing and filtering
Next Steps
You now have a solid foundation in Go's conditional statements. In the next section, we'll explore Go's switch
statements, which provide a powerful and flexible way to handle multiple conditions and make your code more readable and maintainable.
Understanding conditional statements is crucial for implementing business logic and controlling program flow. These concepts form the foundation for all the more advanced programming techniques we'll cover in the coming chapters.
Ready to learn about switch statements? Let's explore Go's powerful switch
statement and learn how to handle multiple conditions elegantly!