I can answer your questions.
1) No, the <<
method is not syntactic sugar for push
. They are both methods with different names. You can have objects in Ruby that define one but not the other (for example String).
2) For a normal method like <<
, the only thing that can happen as a result of a << x
is that the object that a
is pointing to gets modified. The statements a << x
or a.push(x)
cannot create a new object and change the variable a
to point at it. That's just how Ruby works. This kind of thing is called "calling a method".
The reason that +=
being syntactic sugar matters is that means it can be used to modify a variable without mutating the old object that the variable used to point to. Consider a += x
. That statement can modify what object a is pointing to because it is syntactic sugar for an actual assignment to a
:
a = a + x
There are two things happening above. First the +
method is called on a
with one argument of x
. Then the return value of the +
method, whatever it is, is assigned to the variable a
.
3) The reason that your Array case is different is because you chose to mutate the array instead of creating a new array. You could have used +=
to avoid mutating the array. I think that these six examples that will clear things up for you and show you what is possible in Ruby:
Strings without mutations
a = "xy"
b = a
a += "z"
p a # => "xyz"
p b # => "xy"
Strings with mutations
a = "xy"
b = a
a << "z"
p a # => "xyz"
p b # => "xyz"
Arrays without mutations
a = [1, 2, 3]
b = a
a += [4]
p a # => [1, 2, 3, 4]
p b # => [1, 2, 3]
Arrays with mutations
a = [1, 2, 3]
b = a
a.concat [4]
p a # => [1, 2, 3, 4]
p b # => [1, 2, 3, 4]
Integers without mutations
a = 100
b = a
a += 5
puts a # => 105
puts b # => 100
Integers with mutations
Mutating an integer is actually not possible in Ruby. Writing a = b = 89
actually does create two copies of the number 89, and the number cannot be mutated ever. Only a few, special types of objects behave like this.
Conclusion
You should think of a variable as just a name, and an object as a nameless piece of data.
All objects in Ruby can be used in an immutable way where you never actually modify the contents of an object. If you do it that way, then you don't have to worry about the b
variable in our examples changing on its own; b
will always point to the same object and that object will never change. The variable b
will only change when you do some form of b = x
.
Most objects in Ruby can be mutated. If you have several variables referring to the same object and you choose to mutate the object (e.g. by calling push
), then that change will affect all the variables that are pointing to the object. You cannot mutate Symbols and Integers.