Reputation: 141470
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
Reputation: 24020
I strongly recommend reading C passes by reference Java and Ruby don't
Upvotes: 1
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
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
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
Reputation: 149804
They don't point to the same memory location. Ruby doesn't pass by reference.
Upvotes: 1