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
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
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.
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