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?
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