Explicit vs. concise code in Ruby
Posted by
Werner Schuster
on
Jul 30, 2007
- Ruby
- Topics
-
Programming
- Tags
-
Coding Standards
,
-
Language Features
,
-
Troubleshooting
Piers Cawley writes about a potential problem he discovered in a blog article about
Lazily Initialized Attributes. The problematic code:
def content
@content ||= []
end
This is aimed to allow for lazily initialized attributes of a class. In this case, the
@content instance variable is initialized with
[] when it's accessor method
content is called, unless it has already been initialized. The
||= operator means "if the left hand variable is
nil, set it to the right hand value, otherwise just return the left hand variable's value".
However, as Piers points out, there is a problem with certain values due to the way Ruby treats boolean values and
nil. Here an example to illustrate:
a = false
a ||= "Ruby"
What's the result of this? Since a was initialized in the first line, the second line should not have had any effect. However, executing that code reveals that
a now has the value "Ruby", instead of
false.
The problem becomes clear by remembering the common way to write
nil checks in Ruby:
if name
puts name.capitalize
end
In Ruby, a
nil is interpreted as boolean
false, so the code in the
if clause will only run if the
name is not
nil.
While this is usually not a problem, in the lazily initialized attributes code, it's a problem if a legal value for attribute is either
nil or
false. In that case, an access would reset the variable to it's default value.
This is certainly a corner case, however it's the kind of issue that can cause long debugging sessions, trying to figure out why some attributes are occasionally reset while others aren't.
Piers
offers a more explicit version of the code:
def content
unless instance_variable_defined? :@content
@content = []
end
return @content
end
This only initializes the variable if the variable hasn't been defined yet.
This little example could be blamed on Ruby and some of it's language features - but
it's widely known which type of workers blame their tools instead of themselves. While the conciseness of Ruby is very helpful, there are cases where more explicit expression of intent is safer. In this case, the
||= wasn't the right solution and instead the initialization code is supposed to check if the variable had been defined yet.
Have you been bitten by issues such as this one? Are there Ruby features you like to avoid to prevent subtle errors?
Nice post
by
Stephan Schmidt
Posted
Jul 30, 2007 11:55 AM
8 comments
Watch Thread Reply