&& binds tighter than ||
Question
What's the return value of the following Ruby 3 code?
x = nil
y = "Ruby"
x || y && "Rails" # => ???
The correct answer is
-
"Ruby" -
"Rails"Correct -
nil -
false
Explanation
TL;DR
&& has higher precedence than ||, so the expression parses as x || (y && "Rails"). x is nil, so || moves on to its right operand. y is truthy, so y && "Rails" returns "Rails", and that is the value of the whole expression.
Step by step
x = nil
y = "Ruby"
x || y && "Rails" # => "Rails"
x || (y && "Rails") # => "Rails" (same parse, made explicit)
(x || y) && "Rails" # => "Rails" (different parse, same result here)
- Ruby parses the expression as
x || (y && "Rails")because&&binds tighter than||. ||evaluates its left operand first:xisnil, which is falsy, so the result comes from the right operand.y && "Rails":yis"Ruby", truthy, so&&evaluates and returns its second operand,"Rails".
Note that || and && return operands, not booleans. a || b returns a if a is truthy, otherwise b; a && b returns a if a is falsy, otherwise b. That is what makes idioms like name = input || "default" work.
The left-to-right misreading, (x || y) && "Rails", happens to give "Rails" here too, which makes this trap easy to miss. Change the data and the parses diverge:
x = "Ruby"
x || nil && "Rails" # => "Ruby" (nil && "Rails" is never evaluated)
(x || nil) && "Rails" # => "Rails"
Edge cases
The word forms and and or have much lower precedence than &&, ||, and even assignment. Mixing them with = is a classic bug:
a = false || "value"
a # => "value"
b = false or "value" # parses as (b = false) or "value"
b # => false
Reserve and/or for control flow (do_thing or raise "failed") and use &&/|| inside expressions.
Share this quiz
Comments
No comments yet. Be the first.