I recently stumbled over a quirk in the way Ruby handles local variables that I find somewhat dangerous.
Consider:
def salutation(first_name, last_name = nil)
if last_name
full_name = "#{first_name} #{last_name}"
end
"Hi #{full_name}"
end
This is obviously wrong, full_name
is unset when last_name
is nil.
However, Ruby will not raise an exception. Instead, full_name
will simply be nil
, and salutation('Bob')
returns 'Hi '
.
The same would happen in an else
branch:
def salutation(first_name, last_name = nil)
if last_name
full_name = "#{first_name} #{last_name}"
"Dear #{full_name}"
else
"Hi #{full_name}"
end
end
Again, the else
branch would not raise.
Explanation
if
blocks do not actually open a new scope for local variables.
The fact that full_name
is defined as a local variable inside the if
branch is enough to make it available in the rest of the method. It does not matter that the code in the if
branch is never reached.
Note that this is not quite the same as "hoisting" in JavaScript. The variable is not available in the whole block, just in lines after its declaration.