A gotcha of Ruby variable scoping

Posted . Visible to the public.

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.

Tobias Kraze
Last edit
Tobias Kraze
License
Source code in this card is licensed under the MIT License.
Posted by Tobias Kraze to makandra dev (2024-09-05 10:10)