Block Variable Scope in Ruby

I was doing some reading on Ruby and came across some interesting details regarding how block scoping works. Most of it is pretty intuitive – especially if you’re used to scoping in JavaScript – but there were one or two features which I haven’t come across before.

Basics First

First, let’s try some of the basics. As you would expect, the local variables created in the blocks are not available outside them.

(1..5).each do |x|
  print x.succ
end
x # undefined local variable or method `x' for main:Object

Also, as you would expect, variables created outside the block are available within them. (This bit of code calculates the first 10 Fibonacci numbers)

current_val, next_val, count = 0, 1, 1
begin
  print "#{current_val} "
  current_val, next_val = next_val, current_val + next_val
  count += 1
end while count <= 10

This feels pretty intuitive to me – the variables outside the block can be accessed and updated in the block.

In terms of variables declared outside the scope of the block – they don’t actually need to be accessed – the Ruby interpreter only needs to see them on the left side of an assignment.

if false
  name = "Bob"
end
('Ted'..'Zed').each do |n|
  name = n
end
print name # Zed

To be fair, this isn’t really a weirdness with blocks – it’s simply a case of the Ruby interpreter creating the variable even though the assignment isn’t actually executed.

defined? name # nil
if false
  name = "Bob"
end
defined? name # local-variable

Block-local Variables

Block parameters are always scoped local to the block – again, this is pretty intuitive.

current = "2012"
1.upto(10) do |current|
  print current * current
end
print current # 2012

But, as we saw before – we can override variables outside the block.

current = "2012"
1.upto(10) do |i|
  current = i
  succ = current + 1
  print "#{current}, #{succ}"
end
print current # 10

Ruby 1.9 introduced the concept of block-local variables – simply list them in the block’s parameter list, preceded by a semicolon.

current = "2012"
1.upto(10) do |i; current|
  current = i
  succ = current + 1
  puts "#{current}, #{succ}"
end
puts current # 2012

Conclusion

Block variable scope in Ruby is pretty intuitive – especially for those of us who are familiar with similar functionality in JavaScript. The newer block-local functionality in Ruby 1.9 is nice, but it’s only going to be useful in some niche scenarios. I would rather use more descriptive variable names to avoid overriding variables by accident.

Happy coding.