Most of the time, Ruby methods come with two access points—like the doors into and out of a room. The argument list provides the way in; the return value provides the way out. Modifications made to the input arguments do not affect the original data for the simple reason that when Ruby evaluates an expression, the result of that evaluation creates a new object, so any changes made to an argument affect only the new object, not the original piece of data. But there are exceptions to this rule, which I’ll show you now.
Let’s start by looking at the simplest case—a method that takes one value as a named parameter and returns another value:
in_out.rb
def change( x ) x += 1 return x end
On the face of it, you might think you are dealing with a single object, x
, here: The object x
goes into the change
method, and the same object x
is returned. In fact, that is not the case. One object goes in (the argument), and a different object comes out (the return value). You can easily verify this using the object_id
method to show a number that uniquely identifies each object in your program:
num = 10 puts( "num.object_id=#{num.object_id}" ) num = change( num ) puts( "num.object_id=#{num.object_id}" )
The identifier of the variable, num
, is different before and after you call the change
method. This shows that even though the variable name remains the same, the num
object that is returned by the change
method is different from the num
object that was sent to it.
The method call itself has nothing to do with the change of the object. You can verify this by running method_call.rb. This simply passes the num
object to the change method and returns it:
method_call.rb
def nochange( x ) return x end
In this case, the object_id
is the same after num
is returned as it was before num
was sent to the method. In other words, the object that went into the method is the same object as the one that came out again. That leads to the inevitable conclusion that there is something about the assignment in the change
method (x += 1
) that caused the creation of a new object.
But assignment itself isn’t the whole explanation. If you simply assign a variable to itself, no new object is created:
assignment.rb
num = 10 num = num # a new num object is not created
If you now display the object_id
of the num
variable, the number is the same before an after assignment, proving that this really is the same object. So, what if you assign to the object the same value that it already has?
num = 10 num = 10 # a new num object is not created
Once again, the object_id
is unchanged after the assignment. This demonstrates that assignment alone does not necessarily create a new object. Now let’s try assigning a new value:
num = 10 num += 1 # this time a new num object is created
This time, if you display num.object_id
before and after the assignment, you will see a different number—say 21 before and 23 after. The actual numbers are automatically determined by Ruby and may vary. The important thing to understand is that a different object ID indicates a different object. If the same variable returns a different object_id
when a value is assigned to it, that means a new object has been created.
Most data items are treated as unique, so one string “hello” is considered to be different from another string “hello,” and one float 10.5 is considered to be different from another float 10.5. Thus, any string or float assignment will create a new object.
But when working with integers, only when the assignment value is different from the previous value is a new object created. You can do all kinds of complicated operations on the right side of the assignment, but if the yielded value is the same as the original value, no new object is created.
num = 11 puts( "num.object_id=#{num.object_id}" ) num = (((num + 1 - 1) * 100) / 100) puts( "num.object_id=#{num.object_id}" )
In the previous code, the first assignment creates a new num
object with the integer value 11. Even though the result of a fairly complex expression is used in the next assignment, this still has the value 11. Since the value of num
is not changed, no new objects are created, and its object_id
remains the same:
num.object_id=23 num.object_id=23