Multiple references in Ruby

I expected the following code to print "8", "111" and "999". I supposed that each a, b, c and d points to the same memory location. If I change the location through one of them, why would the other not to change? Clearly, my logic is poor, or I overlooked something. It prints "7", "7" and "8", instead.

Why?

a=b=c=d=7
     b  =  8
     puts d

     c  = 111
     puts a

     d  =  999
     puts b

[Clarification]

The reason for my confusion is the example in the book (page 20). They change there similarly the values, but they get the results I suggested above. Are we speaking of the same issue?

Upvotes: 1

Views: 1364

Answers (5)

khelll
khelll

Reputation: 24020

I strongly recommend reading C passes by reference Java and Ruby don't

Upvotes: 1

Vincent Robert
Vincent Robert

Reputation: 36150

a=b=c=d=7
# a, b, c and d points to the same integer object "7"
     b  =  8
# b now points to a new object "8"
# "=" does not change the value of the pointer integer, 
# it assings a new reference like in the line above
     puts d
# obviously, d still points to "7"

     c  = 111
# c now points to another integer object "111"
     puts a
# a still points to "7"

     d  =  999
# d now points to a new integer object "999"
     puts b
# b still points to "8"

in Ruby, the Integer object is immutable so you cannot assign an Integer to multiple reference and change its value after.

As @pts suggested, you should use an array to wrap your Integer reference because Arrays are mutable to you are able to change the value after.

a=b=c=d=[7]
b[0] = 8
puts d[0]
c[0] = 111
puts a[0]
d[0] = 999
puts b[0]

CLARIFICATION:

If you come from a C++ background, it may be strange because C++ does 2 things with the same syntax, assigning the reference and changing the value referenced.

int a = 10; // creates an int on the stack with value 10
int& b = a; // creates a reference to an int and references the a variable
b = 5; // change the value referenced by b (so a) to 5
// a and b now hold the value 5

In Ruby, reference are mutable and integers are not (exactly the contrary to C++). So assigning a reference will actually change the reference and not the referenced value.

Another solution would be to create a class that is a mutable integer:

class MutableInteger
  attr_writer :value
  def initialize(value)
    @value = value
  end
  def inspect
    value
  end
  def to_i
    value
  end
  def to_s
    value
  end
end

a = b = MutableInteger.new(10)
a.value = 5
puts b
# prints 5

Upvotes: 11

Nick
Nick

Reputation: 7760

After the first line, a, b, c and d all point to the same Fixnum object (with value 7). However, when you execute b = 8, b now points to a new Fixnum object (with value 8).

Effectively you're assigning b to a new object, rather than mutating the existing object. This is why your changes are not being propagated as you expected.

If you're comparing with C++, this is like assigning a pointer by value, rather than assigning by reference.

Upvotes: 1

pts
pts

Reputation: 87461

The easiest way to get the output you expect is using a single-element array:

a=b=c=d=[7]
b[0] = 8
puts d[0]
c[0] = 111
puts a[0]
d[0] = 999
puts b[0]

To get if a and b refer to the same object, use a.__id__ == b.__id__ .

Upvotes: 2

Mathias Bynens
Mathias Bynens

Reputation: 149804

They don't point to the same memory location. Ruby doesn't pass by reference.

Upvotes: 1

Related Questions