Reputation: 19
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
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