views:

480

answers:

5

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?

+1  A: 

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

Mathias Bynens
Well, it's not so simple. If you're passing an array or hash to a method, you're passing by reference in the sense that you're not making a copy of it, so if the method modifies the array, you're modifying the original. But yea, you can't fiddle around with pointers and tell variables to point to the same memory location, etc.
kch
-1 True that Ruby does not pass by reference. But since every variable is actually a reference to an object, there are no object copying until you use #dup. After the OP's first line, every variable points to the same object "7".
Vincent Robert
That blog post is missing the point, of course Java and Ruby pass by reference. Passing by reference means the memory contents are not copied into the method parameters, instead the reference is.The real difference is how affectation works: in C/C++ you can directly change the referenced contents, but in Ruby affectation will only change what the variable contains (i.e. replace a ref by another one). So actually I'd say C/C++ affect by reference, Java/Ruby affect by value (of the variable).
Damien Pollet
No the blog post is not missing the point. Java do pass by value and you should keep that in mind when working with native types instead of objects. But since Ruby have objects for everything, it is not a concern anymore.
Vincent Robert
+2  A: 

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

pts
I was looking for it. Great thanks!
Masi
+11  A: 
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
Vincent Robert
you could do the EVIL thing and override the assignment operator on integer ...
Sam Saffron
Assignment is not a message, since it concerns the variable itself and not its contents.
Damien Pollet
+1  A: 

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.

Nick
+1  A: 

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

khelll