UPDATE: For a newer, better take on this topic, check out this post.
If you use Ruby long enough, you will discover the
or operators. These appear at first glance to be synonyms for
||. You will then be tempted to use these English oprators in place of
||, for the sake of improved readability.
Assuming you yield to that temptation, you will eventually find yourself rudely surprised that
or don’t behave quite like their symbolic kin. Specifically, they have a much lower precedence. At this point, you may decide to swear off the use of
or as too confusing.
But that would be doing your code a disservice.
or are useful operators; you just need to understand their special place in Ruby programs.
or originate (like so much of Ruby) in Perl. In Perl, they were largely used to modify control flow, similar to the
unless statement modifiers. A common Perl idiom is:
do_something() or die "It didn't work!";
or keywords serve the same purpose in Ruby. Properly understood,
or are control flow operators, not boolean operators.
and is useful for chaining related operations together until one of them returns
false. For instance:
post = Post.find_by_name(name) and post.publish!
Here, the post will only be published if it is found, due to the short-circuiting nature of
and. How does this differ from
&&? Let’s take a look at an even simpler example:
foo = 42 && foo / 2
The intent here is to assign a variable and then divide it by 2. Since we are just assigning a constant value on the left side, both sides of the
&& will always be evaluated, right? Let’s give it a try:
NoMethodError: undefined method `/' for nil:NilClass from (irb):18 from :0
Was that what you expected? As it turns out, with the relatively high operator precedence of
&&, the way that code is actually parsed looks like this:
foo = (42 && foo) / 2
…which is clearly not what we want. Contrast that to the
foo = 42 and foo / 2 => 21
…and now we have the answer we were expecting.
Another way of thinking about
and is as a reversed
if statement modifier:
next if widget = widgets.pop
widget = widgets.pop and next
or, likewise, is useful for chaining expressions together. The best way to think about the chains constructed with
or is as series of fallbacks: try this, if that fails try this, and so on. For instance:
foo = get_foo() or raise "Could not find foo!"
You can also look at
or as a reversed
unless statement modifier:
raise "Not ready!" unless ready_to_rock?
ready_to_rock? or raise "Not ready!"
or, despite an apparent similarity to
||, have very different roles.
or are control-flow modifiers like
unless. When used in this capacity their low precedence is a virtue rather than an annoyance.
UPDATE: Simplified and clarified some examples based on feedback; added comparisons to
unless statement modifiers.
UPDATE 2: Preston Lee delivers a much more thorough history lesson on the Perl origins of these operators.