Eric Didier Bolton
Eric Didier Bolton

Reputation: 19

Golang switch statement only calls function once

I encountered a rather strange bug writing a program in Go.

Essentially, I have a switch statement where each case is supposed to call a function, setill, twice. But when the relevant case runs, it only calls the function once.

Here's the code snippet:

    check := true
    n, e, s, w := b.North, b.East, b.South, b.West
    switch {
    // NE (>)
    case n.Closed && e.Closed:
        check = check && p.setIll(s)
        check = check && p.setIll(w)
    // NS (\\)
    case n.Closed && s.Closed:
        check = check && p.setIll(e)
        check = check && p.setIll(w)
    // NW (^)
    case n.Closed && w.Closed:
        check = check && p.setIll(e)
        check = check && p.setIll(s)
    // ES (v)
    case e.Closed && s.Closed:
        check = check && p.setIll(n)
        check = check && p.setIll(w)
    // EW (//)
    case e.Closed && w.Closed:
        fmt.Println("Running setIll the first time")
        check = check && p.setIll(n)
        fmt.Println("Running it again")
        check = check && p.setIll(s)
        fmt.Println("And now we're done running")
    // SW (<)
    case s.Closed && w.Closed:
        check = check && p.setIll(n)
        check = check && p.setIll(e)
    }

Here's setIll:

func (p Player) setIll(n *Node) bool {
    fmt.Println("I'm running!")
    p.Illegal.Nodes[n.Loc.X][n.Loc.Y].Closed = true
    return !p.Forced.Nodes[n.Loc.X][n.Loc.Y].Closed
}

This produces the following output:

Running setIll the first time
I'm running!
Running it again
And now we're done running

Notice that "I'm running!" only appears once in the output. Any idea why this may be?

Upvotes: 1

Views: 1683

Answers (1)

twotwotwo
twotwotwo

Reputation: 30087

It's not the switch tripping you up, it's how && works.

&& and || are short-circuiting operators: they don't execute what's on the right-hand side at all if the left-hand result is enough to determine what the answer will be. If your expression were a && f() and a were false it's not necessary to run f() to see that the end result will be false too. The way the Go spec puts this is "The right operand is evaluated conditionally."

This is common across a lot of languages. It's helpful when your first check has to pass for it to make sense to run the others. For example, say you want to check user permissions in a Web app, but only if a user is logged in at all (user != nil): user != nil && user.HasPermission(requiredPerm) does what you need.

If you want to set the check var the same way you're doing it now, but with setIll always called twice, you can assign the setIll results to variables outside of any && expression:

ck1, ck2 := p.setIll(n), p.setIll(s)
check = check && ck1 && ck2

Upvotes: 3

Related Questions