Some basics of variable scope in Ruby
What is variable scope? According to David Black in The Well-Grounded Rubyist, variable scope
refers to the the reach or visibility of identifiers, specifically variables and constants. Different types of identifiers have different scoping rules: using, say, the identifier
xfor a local variable in each of two method definitions has a different effect than using the global variable
$xin the same two places, because local and global variables differ as to scope.
So, when we ask for the value of some variable
x, the answer we get depends not only on where we originally defined
x, but also on where we are when we are asking for
x. This confused me at first, but after learning more about variable scoping, it makes a lot more sense.
Local and global variables
For example, here we define a local variable
name and a global variable
$ indicates that it is a global variable).
Ok, simple enough. To check their values, we can use
No surprises there. We can easily assign a new value to
name, too, and again check that the value has changed.
Here’s where things get a little more interesting. Here’s a method
name_changer that, when given a string
new_name, will assign the value of
new_name to both
$name. From within the method,
puts prints the value of each variable before and after the assignments.
Here’s what happens when we use this method to change
$name to Tina.
Success, right? From within the method, it looks like both
$name have been changed to Tina. But if we check them again outside of the method…
… only the global variable
$name was changed! The local variable
name wasn’t updated after all. Global variables have global scope, meaning that they can be accessed from anywhere (inside or outside of a method, for example). In contrast, our local variable can only be accessed and changed locally. The
name within the method, along with any changes made to it within that method, is only accessible within that method call.
It’s a little confusing that variables with the same name can be so independent of each other. However, this actually protects us from accidently writing over or changing variables from various parts of our program. If you wanted a variable to be easily changeable from anywhere, you could use a global variable instead, but I have never, ever, ever seen any recommendation for doing so.
But what if you did want to use a single variable to keep track of some value, and that value needed to be updated from multiple places? There’s probably a better way to do that than using global variables, and one of these ways becomes obvious from the distinction between instance and class variables, and their relative scopes.
Instance and class variables
Here’s snippet of code defining a
Dog class, modified from an earlier post about Ruby classes.
Every new instance of the
Dog class is initialized with a name and a random friendliness score between 1 and 10. The single
@ symbol, as in
@name indicates that this is in instance variable. As such, the scope of this variable is limited to this specific instance of
Dog. This means every individual dog can have its own name (and its own friendliness score).
But let’s say we also wanted to keep track of how many dogs have been created. We could do this with a global variable, updating each time a new
Dog object is created, but there is a better way: class variables. Class variables are identified by the double
@ symbols, as in
@@dog_count in the
Unlike instance variables, which are only accessible by their corresponding instance of
Dog, class variables are accessible and shared by all
Dogs. Every time a new
Dog is created,
@@dog_count is incremented by 1.
So class variables have a wide enough scope to allow us to keep track of all of our dogs, without being so accessible that they can just be changed from anywhere (like a global variable). They give just the right level of access to just the right kind of objects.
Understanding differences in variable scope across local, global, instance, and class variables will help you maintain sanity when debugging your code and protect you from accidental changes. For more on variable scope in Ruby, check out these resources: