Ruby Demystified: and vs. &&

When I first started learning Ruby, I was excited to discover that Ruby has the keywords and and or as boolean operators. I quickly got into the habit of writing all of my boolean statements with these, instead of the more conventional && and ||, because my code became so much more readable. I thought the word versions of the boolean operators were part of Ruby’s mission to make code more like spoken language. For example, the following is valid Ruby syntax, and you don’t have to know the language to know what it does.

 eat_cereal() if self.hungry? and self.lazy? 

However, the story about Ruby’s boolean operators is not quite that simple. If you’re like me and you get excited over the idea of clean, readable code, you might run off and mass-replace all of your &&’s with and’s, and your ||’s with or’s. Before you do that, open up your favorite Ruby console (I’m partial to pry, myself) and try the following:

 pry(main)> a = true && false => false pry(main)> a => false 

Now let’s try doing it the cool way:

 pry(main)> a = true and false  => false pry(main)> a => true 

Is Ruby really trying to tell me that true and false evaluates to true? Nope. It turns out Ruby just has different precedence rules for and and &&. Now, this example may prompt you to avoid the word versions of the boolean operators altogether, and you wouldn’t be alone — the first Google result when searching “Ruby Style Guide” suggests you should never use them. But I say fear not, as and and or do have their ideal uses. They are much easier to understand if you stop thinking about them as boolean operators and start thinking of them as control flow operators! Allow me to explain.

The first edition of the Pragmatic Programmer’s book on Ruby has an excellent table listing the order of operations. It’s old and talks about Ruby 1.6, but we’re at Ruby 1.9.3 right now and the rules haven’t changed, and they likely won’t change for a long time. As you can see, and is really just a low-precedence version of &&. Its main purpose is to control the flow of logic, since its low precedence and short-circuiting behavior help ensure that all of its left-hand side gets evaluated before any of the right-hand side does. This is useful when you want to chain operations together and have them execute until one of them returns a false-y value (false or nil).

Let’s revisit the above example with this behavior in mind. Using parentheses to illustrate the order of operations, the following illustrates how && and and operate differently.

 (a = (true && false)) ((a = true) and false) 

Think of the second example like this: “If a = true evalulates to a truth-y value (anything but false or nil), run false. Otherwise, return the result of a = true.” It could have just as easily been written like this.

 false if puts true 

The most common use for the and operator is for operations that depend on the success of other operations. Something like this.

 compile_code() and assemble_binary() and install_binary() 

The low-precedence or works in just the same way, short-circuiting execution of its right-hand side if its left-hand side evaluates to a truth-y value (anything but false or nil). It is most commonly used to throw errors if something goes wrong.

 solve_puzzle() or raise RuntimeError "This code just died." 

The above example could have just as easily been written like this, depending on your style.

 raise RuntimeError "This code just died." unless solve_puzzle() 

I prefer the or version in this case as the code gets run from left to right, the same way it is read.

Watch out, though, as the and and or keywords still have slightly higher precedence than their more mainstream control-flow cohorts (if, unless, while, and until).

 eat_cereal() if self.hungry? and self.lazy? 

…gets evaluated in this order:

 (eat_cereal() if (self.hungry? and self.lazy?)) 

A final important point is that and and or have the same precedence, unlike their symbolic counterparts where && evaluates before ||. This means that operations chained together with with and and or always get evaluated from left to right.

 cook_beef() or cook_chicken() and cook_veggies() 

…gets evaluated in this order:

 (cook_beef() or cook_chicken()) and cook_veggies() 

And here’s what they would look like with the symbolic boolean operators.

 cook_beef() || cook_chicken() && cook_veggies() 

…which gets evaluated in this order:

 cook_beef() || (cook_chicken() && cook_veggies()) 

The main thing I would take away from this is to never be afraid of using and and or. They may not be suitable for regular boolean logic, but they have their place when they are used to control the flow of your code, often making it even more readable.

Happy coding! Let us know your thoughts in the comments. :)

Angel Irizarry

Angel Irizarry is the Software Samurai of Tinfoil Security, and a self-proclaimed software purist. All he needs to do his best work is a plain Linux machine with Git and Emacs installed. He loves everything about front-end development, like making pages interactive and super fast, even if that means digging in and optimizing some SQL. When he's not writing code, which isn't very often, you'll find him on his iPad scouring his RSS feeds for news and rumors of cool new gadgets.