Variable scope in Ruby

One big difference between Perl and Ruby is that Ruby has no lexical variables. This confuses me when programming Ruby, because I’m used to thinking ifwill create a new scope. It doesn’t. Variables defined inside if will be available outside, because they belong to the method scope.

For example, in Ruby you can do this:

def foo
  if true
    x = 2
  end
  puts x
end

foo

This prints 2.

However, in Perl:

sub foo {
  if (1) {
    my $x = 2;
  }
  print $x;
}
foo();

You get the warning Use of uninitialized value $x in print at lexical_test.pl line 5..

This is OK. If anything, the Ruby version looks more convenient. But I prefer my variables to have as small a scope as possible, because it avoids mistakes like this:

def foo
  x = 1
  if true
    x = 2
  end
  puts x
end

foo # prints 2. what have you done to my x?

In Perl, we’d write it like this:

sub foo {
  my $x = 1;
  if (1) {
    my $x = 2;
  }
  print $x;
}
foo(); # prints 1

So how to avoid it in Ruby? Well, you could create a new method, or you can create a new block (eg. with a lambda or proc). This feels a bit weird though. Maybe a more Rubyish way is to use Object#tap, like so:

def foo
  x = 1
  2.tap do |x|
    puts x # 2
  end
  puts x # 1
end

That gets a bit unwieldy if you have multiple variables. But then, if you need more than 1 variable with the same name in your method, then it’s probably a sign that you should write a new method…

Leave a comment

Your e-mail address will not be published. Required fields are marked *