Ruby guard clause FTW
A common Ruby programming idiom is writing guard
clauses for
immediate returns in lieu of nested if-elsif-elsif-elsif-end
or case
statements.
The Ruby 'guard clause' idiom
The guard clause idiom construction looks like this:
def even? number
return true if number % 2 == 0
false
end
Before going any further, I know Ruby has the even?
method for
doing exactly this. And I could easily generate a guard clause example
using specialized domain knowledge. However, the example provides code everyone
can easily follow, and the discussion is supposed to be focused on guard
clauses, not implementations of Ruby library methods. Just wanted to clear that
up.
The first line of the method is often called a “guard clause” because of the syntactic pattern of returning from the method early, given some condition.
But technically, isn’t a “real” guard clause code for trapping invalid parameters or states on method entry?
In our code above, all the values of the argument number
are
assumed valid, even a nil argument. There is no “guarding” going on.
What is going on, instead, is control of flow. Plain old programming in fact.
The real beef seems to be related to a zombie meme left over from structured programming, as promoted by Dijkstra: never have more than one return from a function, and that return must be the last line of the function.
Our code above seriously violates Dijkstra’s dictum.
Isn’t that a bad thing?
In my opinion, no, it’s not, at least not for the code as written above. It’s a small method, just a few lines. Back when structured programming was adopted, functions commonly had dozens (or even hundreds) of lines. Then it would make a lot of sense to limit each function to a single return statement, and require it to be at the bottom of the function.
But let’s look at the code above again. The return
statements are
almost equivalent to setting a return value on a conditional and jumping
directly to the end of the method to return that value. Like with a
goto
statement.
And there isn’t anything wrong with that. Used correctly,
goto
statements, are an appropriate and even elegant way to
control program flow. The Ruby C source code has goto
s, and
tastefully employed goto
s are an accepted convention in Unix socket
programming. There’s nothing intrinsically wrong with a goto statement.
The upshot is that a leading return may syntactically resemble a guard close, without being a guard clause.
And like any other technique, using it correctly is often as much a matter of taste as anything else.
Errors and exceptions
Suppose we require a guard clause to actually “guard,” what does that look like in Ruby? Given that argument checking should be performed in this method, here’s one way to do it:
require 'rspec'
class Guard
def self.even? number
return true if number%2 == 0
false
rescue NoMethodError => e
raise e, 'bad argument type'
end
end
describe Guard do
it 'instantiates correctly' do
expect(Guard.new).to_not be_nil
end
it 'returns true for an even number' do
expect(Guard.even?(2)).to eq true
end
it 'returns false for an odd number' do
expect(Guard.even?(1)).to eq false
end
it 'blows up for nil argument' do
expect { Guard.even?(nil) }.to raise_error(NoMethodError, /bad arg/)
end
end
Whether or not argument checking should be performed in the Guard.even? method is not relevant to this article; it’s a different discussion. We’re assuming, a priori, that we do need to check the argument.
Note also def-rescue-end
(DRE) instead of
begin-rescue-end
(BRE). The begin
is implied and
removes a level of nesting to increase readability.
The design of the Ruby programming language allows ignoring a type check unless the underlying Ruby interpreter complains. This, in turn, allows us to proceed with computation of the common cases up front, and never worry about the error code unless there is an error. In C or C++ (or Java), this would not be possible, the argument checking would have to be performed on entry to the method. In Ruby that would look like this:
def even? number
raise NoMethodError('bad arg') unless number.is_a? Integer
return true if number % 2 == 0
false
end
But now we’re checking the type every time the even?
method is
invoked. Wasteful and unnecessary. Note that this implementation also passes
the specs given above.
Here’s a bit more discussion in some follow-on links.
More discussion on guard clauses (and the if statements they replace)
Any further investigation or discussion should start by reading Martin Fowler’s “Replace nested conditional with guard clause.” This is a very short article and code heavy. An experienced programmer can read it at a glance. Fowler’s definition of a guard clause is includes all methods with multiple returns on leading conditions. This is more expansive than the definition proposed here, which would restrict guard clauses to code which checks argument validity (type, range, etc.). That said, I use Fowler’s definition when discussing code with programmers.
Here’s the blog post I started to write: “Prefer guard clauses over nested conditionals.” I found this after extending the initial draft of this article to include some test code.
This article provides an example of using a guard clause to guard in the sense discussed above: Single exit point. Rereading, I’ve probably drawn a fair bit of material from the “Single exit point” article, hence, it can also be regarded as a reference for this blog post.
Part of the case for guard clauses comes from programmers exasperated with Alpine if statements. You know, those very long functions with deeply nested conditionals. I find such code difficult to read, but I have worked on a fair bit of legacy code with constructions as vastly ghastly as what’s shown in this article. Possibly not quite as “alpine,” but certainly as convoluted.
The readability of guard clauses depends on the reader. In this article, the author attempts to make a case for guard clauses with a coworker. The coworker finds guard clauses increase the difficulty of understanding the control of flow through a program. The author (and myself) find completely the opposite.
Here is a list of controversial rules for code in an open source Java project. About half way down, notice the rule for having a single return located at the last line of the method. Obviously, this rule precludes guard clause techniques in favor of exception handling. Also notice the wordiness of Java compared to Ruby. Whether this wordiness is a good or a bad thing is a certainly matter of taste. Personally, I prefer the conciseness of Ruby (and Python).
What drives the guard clause idiom?
Flattening the arrow is an excellent starting point for understanding conditions promoting a refactor from nested conditionals to guard clauses. You may find the examples slightly dated, but of major interest is Atwood’s Item #4: Always use an opportunity to return as soon as possible from a function. This is almost polar opposite of Dijkstra’s dictum. The notion of “clean code” changes over time, it’s a cultural artifact, machine don’t care.
Last but far from least, the discussion on Guard clause at (the) wiki is worth investigating in detail, as is the article on “The Arrow Anti Pattern.” Wiki at c2 is, of course, the original wiki. If you don’t know, now you know.
As you can see, guard clauses can be hot button topic with programmers adhering to Dijkstra’s Dictum of single exit points. Your opinion, or more likely, your team’s or team leader’s or bosses opinion will decide the day to day use of this idiom, but at least your opinion should be a bit better informed as a result of reading all the way down to here.