If vs. Unless in Ruby

If vs. Unless in Ruby

Do you find unless to be confusing? I know I do. Especially if it’s combined with one or more boolean operators.

Take this example.

def validation
  "Bingo!" unless email_invalid? || email_missing?
end

Do you find that easy to read? I know I don’t.

Figuring out boolean operator precedence is hard enough, but throwing unless in the mix is just too much for my brain to process.

To me, the following feels much easier to understand.

def validation
  "Bingo!" if email_present? && email_valid?
end

There’s something about if that makes it easier to read and understand.

Unless vs. if

unless is the exact opposite of if. It’s a negated if.

In other words, the following three examples are equivalent.

unless number.even?
  # runs if `number` is NOT even
else
  # runs if `number` is even
end
if not number.even?
  # runs if `number` is NOT even
else
  # runs if `number` is even
end
if !number.even?
  # runs if `number` is NOT even
else
  # runs if `number` is even
end

You won’t see unless...else used very often in real life code because you can always replace it with an if...else statement.

unless true
  # do falsy stuff
else
  # do truthy stuff
end

Can be written as an if...else statement. Like so.

if true
  # do truthy stuff
else
  # do falsy stuff
end

How to use unless

A good use for unless is when you want to check something at the beginning of a method. Also known as a guard clause.

def my_method(name)
  return unless name.empty?

  # ...
end

I find that using the modifier version of unless, as a guard clause improves readability.

What is the value of the if/unless modifier?

If you’re expecting a return value from the modifier, you might be surprised to find that there isn’t one. Let us test this.

What does the following method return if name is empty?

def some_method(name)
  "Bingo!" unless name.empty?
end

some_method("") # => nil

Likewise, what does the following if expression evaluate to when number is 0?

def some_method(number)
  "Bingo!" if number > 0
end

some_method(0) # => nil

As you can see, both of them are nil. But why is that?

Two reasons:

  1. It’s because the expression never runs.
  2. An empty code block returns nil.

So if there is no code to run, the entire block (i.e. the method) returns nil.

Check out these other empty code blocks.

class Foo; end # => nil
begin; end # => nil
eval("") # => nil

If you liked this article, please take a moment and say thanks by sharing it on your favorite social media channel.