Variables point to objects
In Ruby, all variables are pointers to objects, i.e. arrays, hashes, strings, integers.
Take a look at this example from one of the Launch School exercises:
name = ‘Bob’
save_name = name
name = ‘Alice’
puts name, save_name
Look first at the first 2 lines. In the first line, a new variable (name) is initialized to reference the string object “Bob”.
In the next line, save_name points to the variable name. But remember: all variables are pointers to objects. Therefore, save_name also points to an object. The question is what object?
save_name points to what name. What does name point to? The string object “Bob”. So save_name also points to “Bob”
name and save_name point to the same object.
Looking now at the third line, name is now reassigned to “Alice”. name now points to a different place in memory. At this point, note the original name variable still exists, just with a different object id. Also, the original object, having been re-assigned, now cannot be referenced by “name”.
So next question is, what object does save_name now point to? We know name has been re-assigned. But does save_name refer to the second object or first object? Well, we just said the original name variable still exists and it hasn’t been modified. So save_name points to the original name variable, “Bob”.
So puts name returns “Alice”. And puts save_name returns “Bob”.
Now take a look at this, another one of Launch School’s exercises:
name = ‘Bob’
save_name = name
name.upcase!
puts name, save_name
In the second line, a destructive method call is performed on the variable name. We know that destructive method calls mutate, or permanently change, the caller, i.e. the original variable. So in this case, calling upcase! permanently modifies name. So now name is “BOB”. So save_name is also “BOB”.
Therefore
puts name, save_name
returns
“BOB”
“BOB”
Now take this example.
array1 = %w(Moe Larry Curly Chemp Harpo Chico Groucho Zeppo)
array2 = []
array1.each { |value| array2 << value }
array1.each { |value| value.upcase! if value.start_with?(‘C’) }
puts array2
Here, array1 points to an array of strings. array2 points to an empty array.
In the third line, we iterate each element of array1 and perform a destructive method call on array2 with each iteration. Specifically, we append each element of array1 to array2.
Here comes the tricky part.
In the fourth line, we again iterate each element of array1 and perform a destructive method call on each element of array1. Specifically we permanently up-case every word starting with C.
The question is, what does puts array2 output? To minimize confusion, let’s update array1 and array2 first.
array1 = %w(Moe Larry Curly Chemp Harpo Chico Groucho Zeppo)
array2 = %w(Moe Larry Curly Chemp Harpo Chico Groucho Zeppo)
Just looking at the above is deceiving. Because you would think array2, although having the same content as array1, is separate from array1 such that if you modify array1, the change would not affect array2. But the answer isn’t so simple.
array2 actually contains the very same string objects as array1, i.e. they reference or point to the same string objects. They are not new copies of those string objects. They are in fact new references to the original string objects. This explains why whatever happens to the original string objects will now affect the new references.