A program that only stores values isn’t very useful. The whole point is to do something with them — add them, compare them, decide what to do based on their state. This lesson covers the tools Go gives you for that: operators and conditional statements.

Arithmetic operators

The basic math operators work the way you’d expect:

package main

import "fmt"

func main() {
    a := 10
    b := 3

    fmt.Println("Sum:       ", a+b)
    fmt.Println("Difference:", a-b)
    fmt.Println("Product:   ", a*b)
    fmt.Println("Quotient:  ", a/b)
    fmt.Println("Remainder: ", a%b)
}
Sum:        13
Difference: 7
Product:    30
Quotient:   3
Remainder:  1

A few things to notice:

  • Integer division drops the decimal part. 10 / 3 is 3, not 3.33. If you need a decimal result, use float64: float64(a) / float64(b).
  • % is the modulo (remainder) operator. 10 % 3 = 1 because 10 divided by 3 leaves a remainder of 1. It’s enormously useful — for instance, n % 2 == 0 checks if n is even.

Assignment shortcuts

Adding to a variable is so common Go gives you a shortcut:

// inside main()
score := 0

score += 10   // same as: score = score + 10
score -= 3    // same as: score = score - 3
score *= 2    // same as: score = score * 2
score /= 7    // same as: score = score / 7

fmt.Println(score)

There’s also ++ and -- for the very common case of adding/subtracting 1:

// inside main()
count := 0
count++       // count is now 1
count++       // count is now 2
count--       // count is now 1
fmt.Println(count)

Unlike C or JavaScript, count++ is a statement, not an expression. You can’t write x := count++. Keep it on its own line.

Comparison operators

Comparison operators compare two values and return a bool:

package main

import "fmt"

func main() {
    a := 10
    b := 20

    fmt.Println("a == b:", a == b)
    fmt.Println("a != b:", a != b)
    fmt.Println("a <  b:", a < b)
    fmt.Println("a >  b:", a > b)
    fmt.Println("a <= b:", a <= b)
    fmt.Println("a >= b:", a >= b)
}
a == b: false
a != b: true
a <  b: true
a >  b: false
a <= b: true
a >= b: false
OperatorMeaning
==equal to
!=not equal to
<less than
>greater than
<=less than or equal
>=greater than or equal

Comparison works on numbers, strings, and booleans. For strings, it compares lexically — "apple" < "banana" is true.

Logical operators

Combine boolean expressions with these:

// inside main()
isLoggedIn := true
isAdmin := false

fmt.Println(isLoggedIn && isAdmin)   // AND: true only if both are true
fmt.Println(isLoggedIn || isAdmin)   // OR:  true if either is true
fmt.Println(!isLoggedIn)             // NOT: flips the value
false
true
false

Go uses short-circuit evaluation: in a && b, if a is already false, Go never even looks at b. Same for ||: if a is true, b is skipped. This matters when b is an expensive function call or could crash if a was false.

The if statement

if is how you make decisions:

package main

import "fmt"

func main() {
    age := 18

    if age >= 18 {
        fmt.Println("You can vote.")
    }
}
You can vote.

Notice there are no parentheses around the condition — that’s the Go style. The braces { }, however, are required, even for a single-line block. (This is one of Go’s safety rules — it eliminates a whole category of “I forgot the braces” bugs.)

else

An if on its own only handles one branch — what happens when the condition is true. Pair it with else to also handle the case when it’s false:

package main

import "fmt"

func main() {
    var num int

    fmt.Println("Enter a number: ")
    fmt.Scanf("%d", &num)

    if num%2 == 0 {
        fmt.Printf("%d is an even number\n", num)
    } else {
        fmt.Printf("%d is an odd number\n", num)
    }
}
Enter a number:
7
7 is an odd number

When num % 2 == 0 is true, the first block runs; otherwise the else block runs. Exactly one of the two will execute.

This example also sneaks in two things you’ll see often: fmt.Scanf reads input from the terminal using the same verbs as Printf (%d for an integer), and the & in front of num passes its memory address so Scanf can write into it. Pointers get a proper treatment in the next section — for now, just remember that Scanf always wants & in front of each variable.

else if

// inside main()
score := 75

if score >= 90 {
    fmt.Println("Grade: A")
} else if score >= 80 {
    fmt.Println("Grade: B")
} else if score >= 70 {
    fmt.Println("Grade: C")
} else {
    fmt.Println("Grade: F")
}
Grade: C

Conditions are checked top to bottom; the first one that matches runs and the rest are skipped.

if with a short statement

Go has a powerful shortcut: you can declare a variable inside an if, and that variable is only visible inside the if/else blocks:

package main

import "fmt"

func main() {
    var num int

    fmt.Println("Enter a number: ")
    fmt.Scanf("%d", &num)

    if n := num / 2; num%2 == 0 {
        fmt.Printf("%d is an even number; half of it is %d\n", num, n)
    } else {
        fmt.Printf("%d is an odd number; half of it (rounded down) is %d\n", num, n)
    }
    // n is no longer accessible here
}
Enter a number:
7
7 is an odd number; half of it (rounded down) is 3

n := num / 2 runs first, then the condition num%2 == 0 is checked. Both branches can use n, but it’s gone the moment the if/else ends — keeping it scoped tight prevents accidental reuse later.

This pattern is everywhere in Go — especially when checking errors, which we’ll cover later.

The switch statement

When you’d write a chain of if/else if with the same variable, switch is cleaner:

package main

import "fmt"

func main() {
    day := "Tuesday"

    switch day {
    case "Monday":
        fmt.Println("Start of the week")
    case "Tuesday", "Wednesday", "Thursday":
        fmt.Println("Mid-week")
    case "Friday":
        fmt.Println("Almost weekend")
    case "Saturday", "Sunday":
        fmt.Println("Weekend!")
    default:
        fmt.Println("Unknown day")
    }
}
Mid-week

A few things to notice:

  • Each case can match multiple values separated by commas
  • Once a case matches, only its block runs — Go doesn’t “fall through” into the next case automatically (a common gotcha in C and Java). No need for break.
  • default runs if no case matches; it’s optional

Matching characters

A switch shines when you want to accept a few “equivalent” inputs — for example, treating y and Y as the same answer to a yes/no prompt:

package main

import "fmt"

func main() {
    fmt.Printf("Do you want to subscribe? y/n : ")

    var c byte
    fmt.Scanf("%c", &c)

    switch c {
    case 'y', 'Y':
        fmt.Println("Thank you!")
    case 'n', 'N':
        fmt.Println("No Problem!")
    default:
        fmt.Println("Invalid input")
    }
}
Do you want to subscribe? y/n : Y
Thank you!

Two small details worth pointing out:

  • 'y' and 'Y' are character literals — single-quoted characters that compare cleanly against a byte. (Use double quotes only for strings.)
  • fmt.Printf("...y/n : ") has no \n, so the cursor stays on the same line and the user’s keystroke appears right after the prompt. That’s a small but useful trick whenever you’re asking for input.

switch without an expression

You can also use switch as a cleaner alternative to long if/else if chains:

// inside main()
score := 75

switch {
case score >= 90:
    fmt.Println("A")
case score >= 80:
    fmt.Println("B")
case score >= 70:
    fmt.Println("C")
default:
    fmt.Println("F")
}

This pattern is very Go-flavored — many developers prefer it over if/else if chains because it’s easier to scan visually.

What’s next

You can now make decisions in your programs. The remaining piece of basic control flow is repetition — running the same code many times. That’s what loops are for, and that’s what’s next.

Toggle theme (T)