Assuming we have a class that looks like this:
class Foo
attr_reader :options
def initialize options = {}
@options = options
end
end
What does the following code output?
foo = Foo.new nil
puts foo.options.inspect
The output will be {}
, right? Well, let’s check it out:
puts foo.options.inspect
# => nil
What the…? options
was an optional parameter, and we set the default
value to be an empty hash, what gives? What went wrong? Well, let’s try
something a little different:
foo = Foo.new
puts foo.options.inspect
# => {}
This time we get the default value we were expecting, but why didn’t it
work before? Well, with optional parameters, the default value is used if the
argument is omitted; but, if an explicit nil
is passed, that will be
the parameter’s value. This behavior is not just for Hash
default
values, it applies to any default value.
Okay, so knowing that, how do we get around this issue? Well, the answer’s not too bad:
class Foo
attr_reader :options
def initialize options = nil
@options = options || {}
end
end
foo = Foo.new nil
puts foo.options.inspect
# => {}
So, this time we still have an optional parameter; but, we’re declaring the
default value as nil
, and when we set the @options
instance variable
we use ||
to make the value {}
, if options
is nil
. This may seem
like a small change, but can mean a big different in code correctness.
If in some other part of our class we call methods on options
thinking
we’re guaranteed to have a Hash
– or Hash-like object – but we
in fact have nil
, that could lead to some confusing errors.
Okay, so maybe now you are thinking: ‘but, I would never explicitly pass
a nil
like that.’ Well, you may not knowingly do so, but take a look
at the following:
# somewhere else params is defined as: params = {}
Foo.new params[:foo]
In this scenario, maybe you thought you were passing a nested Hash
into
Foo.new
, but if params
doesn’t have a value for the key :foo
,
you’ve just passed a nil
into Foo.new
. This is just one example of
how a nil
could end up being explicitly passed as an argument, but
it’s far from the only one.
I think the rule of thumb for default parameters should be:
- Always declare the default value of an optional parameter as
nil
- Inside the body of the method, set the default value you actually want
And, that’s it really. I think that’s a pretty simple rule.