Ruby Returns, Conditionals, and the 'and' Keyword

If you’re a Ruby developer, chances are you’re at least somewhat familiar with the and keyword. Not only that, but it’s pretty likely that you’ve worked in a Rails project and have seen something like this:

def login
  @user = User.find(params[:id])
  redirect_to create and return if @user.nil?
end

Cool. A nifty one liner. There’s more to this story that we’ll come back to in a bit. Have you also seen code like this?

def can_edit?
  if @user.is_admin? and @post.is_editable?
    true
  else
    false
  end
end

Bear with me. These examples are terrible, but I think you’ll get the idea. We have a statement that requires two conditions that must both be true.

Now, what happens if is_admin? (or the equivalent in your code) can return nil? Let’s jump ahead a little bit and skip to using some ternaries to return a value into a variable, depending on the input.

nil and nil ? 'truth' : 'false'
# returns nil

Weird. What if we had the same inputs but had used a double ampersand?

nil && nil ? 'truth' : 'false'
# returns "false"

Okay. Something funky is definitely going on.

It’s at this point, that I’d like to make a quick note. If you’re not using Rubocop , I would strongly encourage that you do. Some people like to add it in to their editor, others don’t want it cluttering their work space. Rubocop will warn about using and in your conditionals, probably to avoid weirdness like what we’re seeing.

Let’s take a look at some more examples (note the quotes, they denote strings!):

true and false ? 'truth' : 'false'
# returns "false"

true && false ? 'truth' : 'false'
# returns "false"

true and nil ? 'truth' : 'false'
# returns "false"

true && nil ? 'truth' : 'false'
# returns "false"

nil and nil ? 'truth' : 'false'
# returns nil

nil && nil ? 'truth' : 'false'
# returns "false"

nil and [] ? 'truth' : 'false'
# returns nil

nil && [] ? 'truth' : 'false'
# returns "false"

1 and nil ? 'truth' : 'false'
# returns "false"

1 && nil ? 'truth' : 'false'
# returns "false"

1 and 1 ? 'truth' : 'false'
# returns "truth"

1 && 1 ? 'truth' : 'false'
# returns "truth"

[] and [] ? 'truth' : 'false'
# returns "truth"

[] && [] ? 'truth' : 'false'
# returns "truth"

0 and true ? 'truth' : 'false'
# returns "truth"

0 && true ? 'truth' : 'false'
# returns "truth"

0 and false ? 'truth' : 'false'
# returns "false"

0 && false ? 'truth' : 'false'
# returns "false"

false and false ? 'truth' : 'false'
# returns false

false && false ? 'truth' : 'false'
# returns "false"

I don’t know about you, but I found this very surprising in some cases! While we’re only looking at and vs. &&, very similar things happen when you use or instead of ||. I won’t dive into it here, because I want to take a look at using a method with and return after it.

Check this out:


def hmmm(truth)
  puts('first') and return if truth
  puts('second')
end

irb: > hmmm(true)
first
second
 => nil

Wait, why did ‘second’ get printed out?

via GIPHY

Turns out that puts returns nil after it prints the input text (as you can see from line 9 from the “things_that_make_you_go_hmm.rb” gist)!

So, whether you’re writing conditionals or trying to return after calling a method, be careful about what the value is that is before your and or or.

I hope this article was helpful to you as you deal with conditionals and return values! Until next time!

Published December 7, 2017 by Toby Chin