How do method variables work in Ruby
May 18, 2013
The inspiration of this blog post has been a recent question on Stack Overflow about whether Ruby passes the method variables by value
or by reference
. The short answer, is that it is passed by reference
, or perhaps more accurate by "called by sharing"
Let's take a look at the example (slightly modified) that was causing the confusion. In the below example, it looks like the value is being passed, as the address
variable hasn't been changed by the down_case
method.
address = "123 ABC strEet"
def down_case(address)
address= address.downcase
end
puts address
=>123 ABC strEet
down_case(address)
=> 123 abc street
puts address
=>"123 ABC strEet"
That is incorrect. There are a couple of things going on, so let me take you on a journey (stay with me)
- how object reference works
- how scope of method variables works
- how passing the object's reference works in methods
How Object References Work
An object can have multiple references to it; any of these references can be used to send messages to that object to change its state.
In the example below. I created an object with the value "I am an object" that the variable a
references. I later make b
point to the same object.
Both of these references point to the same object. If you change a's
objects it also changes b's
object as they reference the same object.
a="I am an object"
b=a
puts b #=> "I am an object"
puts a.object_id #=> 70307771825720
puts b.object_id #=> 70307771825720
How Scope of Method variables works
The method variable is local to the method even if it shares the same name as variable outside the method.
In my example above, the address
variable exist both in the method scope and the outer scope.
The advantage here is that the same name in a method variable will not modify the outer scope variable by “accidentally” re-assigning it. However, in the original example, the user wanted to change the outer scope variable, but instead change the inner scope address
reference to point to the new "123 abc street" String Object, without impacting the outer scoped address
reference which remained pointing to the "123 ABC strEet" String Object.
How Passing the Object's reference works in Methods
To get the users desired result the method downcase!
could have been used to modify the outer object.
This works by modify the underlying object i.e. modifies the object to "123 abc street” from "123 ABC strEet”. Which is different from downcase
method as it creates a new object with the value "123 abc street" which is assigned the address
reference, with the inner and outer scope address
pointing to different objects with different values.
This passing the reference of object, but not the actual reference is know as called by sharing
address = "123 ABC strEet"
def down_case(address)
address.downcase!
end
puts address
=>"123 ABC strEet"
down_case(address) _
=>"123 abc street"
puts address
=>"123 abc street"
How else could this have been done
You could also use Procs or Lambdas to get a similar results. A Proc has access to the scope that existed when it was created.
address = "123 ABC strEet"
down_case = Proc.new do |a|
address =a.downcase
end
down_case.call(address)
puts address
The downcase
method call creates a new string object with the value "123 abc street" but since the address
reference existed before the creation of the Proc it is “in scope” so can be re-assigned to the new String Object "123 abc street" i.e. this changes the address
reference outside the Proc method.
Summary
We pass in references rather than values to method, but the method variable is local to the function ( see called by sharing for more details) even if they share the same name
If you want to share scope consider using a Proc or Lambda