2

A parameter received by a method is a reference to the same object as the argument passed to it, e.g.:

def object_id_of_param(object)
  object.object_id
end

the_object = [1, 2, 3]

the_object.object_id           # => 15247020
object_id_of_param(the_object) # => 15247020

If you call an instance method of that object that modifies the object in-place from any reference to the object, regardless of scope, the single object that all variables reference is itself modified in-place, e.g.:

def modify_object(object)
  object.map! { |n| n + 1 }
end

the_object = [1, 2, 3]

the_object.object_id # => 18047480
the_object # => [1, 2, 3]

modify_object(the_object)

the_object.object_id # => 18047480
the_object.inspect #=> [2, 3, 4]

If you want to do something else to that object and a method like Array#map! is not available to do it, you can use Object@instance_variable_set to change it (see ndn's great answer).

If you try to change it to a different class entirely, it doesn't work; the following method does not modify the original object. It does not turn it from an Array into a Time; it causes the new object variable in the method to reference a new Time object:

def fail_to_modify_object(object)
  object = Time.now
end

the_object = [1, 2, 3]

the_object.object_id # => 14554660
the_object.inspect #=> [1, 2, 3]

fail_to_modify_object(the_object)

the_object.object_id # => 14554660
the_object.inspect #=> [1, 2, 3]

The example of turning an Array object into a new Time is contrived, but you get the idea.

Is it possible to change an object, referred to by an arbitrary number of variables, into an instance of a different class?

Community
  • 1
  • 1
  • Read this nice article -http://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/ and answers to this question http://stackoverflow.com/questions/1872110/is-ruby-pass-by-reference-or-by-value – Wand Maker Sep 13 '15 at 21:10

3 Answers3

2

Bang methods aren't necessary always modifying objects in place.

Bang is just a convention that says this version of the method is "more dangerous" than the one without the bang. For example, in rails many bang methods raise an exception instead of returning nil.

Also the fact that a method doesn't have a bang doesn't mean it doesn't modify the object (for example Array#pop).

As to how to modify an object that doesn't provide any methods that mutate - you can use Object#instance_variable_get and Object#instance_variable_set:

class X
  attr_reader :foo

  def initialize
    @foo = :bar
  end
end

x = X.new
x.foo # => :bar
x.instance_variable_set(:@foo, :baz)
x.foo # => :baz

However, note that if the someone didn't give you direct access to something, 99.99% of the time you are doing something wrong if you are trying to.

ndnenkov
  • 35,425
  • 9
  • 72
  • 104
  • Cool; this can be used to modify objects in-place in more ways than their implementations present. I'm still more curious to know if it's possible to change objects in truly arbitrary ways, like making them instances of an entirely different class as in the example. If it's possible, though, how it could, should and shouldn't be used aren't relevant to the question. – Thomas Profitt Sep 13 '15 at 20:54
1

To modify an object retaining its identity (object id), the new object must at least belong to the same class as before; otherwise, it does not make sense. It is impossible to modify a Time instance into an Array instance or vice versa retaining its identity.

Or, if you meant in your second example to simply change the reference of the_object from a Time instance into an Array instance, then that is a matter of scope of local variables, and has nothing to do with immutability.

sawa
  • 165,429
  • 45
  • 277
  • 381
1

You need to distinguish between a variable and an object.

In a method, you get access to the object. You don't get access to the variable that is defined outside the method.

If you write object = Time.now, you are not changing the object, you assign another object to the variable object. The old object still exists. The variable outside your method still holds the old object.

You can change the object by sending messages to it. If you have an array object, you can see your variable the methods here: http://ruby-doc.org/core-2.2.0/Array.html Some of the methods change the array, some methods just return something.

You can do things like object[0] = Time.now . Even this is implemented as method call to []= . It will change the first element of your array-object to now hold a time object. So you are quite free to change your array object.

If you are advanced, you can even add new methods to your array-object, so that it may behave like a time object. But that is almost always not a good idea.

The variable only exists where it is defined.

Meier
  • 3,858
  • 1
  • 17
  • 46
  • The question isn't about changing what object an out-of-scope variable references, but about whether it's possible to change an object referenced by any number of variables in arbitrary ways (like changing an Array to a Time) if given one of those variables. Instance methods that change the object in-place can do this, and changing instance variables does this, but you're limited in the scope of what you can change. – Thomas Profitt Sep 13 '15 at 20:59