What does it mean that strings are "mutable" and why care? - ruby

I have been using Ruby on Rails for a while without studying Ruby, now I am taking Odin Project. I am not really sure about the answer of this question:
What does it mean that strings are "mutable" and why care?
Update:
so now I understand mutable string basically means the value in the memory can be changed after string is created.
immutable string means the value in the memory cannot be changed once created, only the reference can be changed.
based on the result of following code:
a = "foo"
a.object_id
=> 70218039369160
b = "bar"
a << b
=> "foobar"
a.object_id
=> 70218039369160
can I say string in Ruby is mutable? because the value in same memory is changed
a += b
=> "foobar"
a.object_id
=> 70218039184800
and the + method in Ruby actually create a new String object instead of change the value of the original String object, that's why the object id changed.
my question is will it cause any security problem if I use += and << interchangeably?

It means that you can modify an exising instance of a string, without constructing a new one. Consider the following code:
str1 = "foo"
str2 = str1
str1 += "bar"
In languages like javascript where strings are immutable, the value of str2 will still be "bar" after that code is executed, as you can see here. However, in languages where strings are mutable, like ruby, when you append "bar" at the end of str1 (using the ruby operator << which does that) you are actually modifying the instance, not creating a new one, so str2 will also be modified, as you can see here.
PS: Note that the append at the end of the string operator in ruby is << instead of += (+= actually creates a new string, but not because it's forced to do it, like in javascript).

Related

What's the difference between String.new and a string literal in Ruby?

I'm having difficulty seeing how the processes for creating the value for a new instance of a String object differs from creating the value for a String literal.
a = String.new("Hello")
# => "Hello"
b = "Hello"
# => "Hello"
and if I run
a == b
# => true
Same values! What's going on?
A string literal is a string object. There's no difference in earlier versions of Ruby.
As of Ruby 2.3 you have the option to have string literals frozen. It's the planned default in Ruby 3.0
this means that...
x = "Hello"
x.upcase!
...will generate an error, because the string is not mutable.
Using the constructor...
x = String.new("Hello")
x.upcase!
Works fine.
== checks for equal content.
equal? checks for equal identity.
a = "hello"
b = "hello"
a == b # => true
a.equal?(b) # => false
In Ruby string literals are not immutable and thus creating a string and using a literal are indeed the same. In both cases Ruby creates a new string instance each time the expressions in evaluated.
Both of these are thus the same
10.times { String.new }
# is the same as
10.times { "" }
Let's verify this
10.times { puts "".object_id }
Prints 10 different numbers
70227981403600
70227981403520
70227981403460
...
Why? Strings are by default mutable and thus Ruby has to create a copy each time a string literal is reached in the source code. Even if those literals are usually rarely modified in practice.
Thus a Ruby program typically creates an excessive amount short-lived string objects, which puts a huge strain on garbage collection. It is not unusual that a Rails app creates 500,000 short-lived strings just to serve one request and this is one of the main performance bottlenecks of scaling Rails to millions or even 100 millions of users.
To address that Ruby 2.3 introduced frozen string literals, where all string literals default to being immutable. Since this is not backwards compatible it is opt-in with a pragma
# frozen_string_literal: true
Let's verify this too
# frozen_string_literal: true
10.times { puts "".object_id }
Prints the same number 10 times
69898321746880
69898321746880
69898321746880
...
Fun fact, setting a key in a hash also creates a copy of a string
str = "key"
hash = {}
hash[str] = true
puts str.object_id
puts hash.keys.first.object_id
Prints two different numbers
70243164028580
70243132639660
In your example a and b are c-like references and not pointers. So you are comparing values not objects.
Same values! What's going on?
Nothing special going on, you can create infinite number of local variables with same value.
You can use Object#object_id to see, that these variables are in fact different objects:
a = String.new("Hello")
b = "Hello"
a.object_id #=> 70295696460580
b.object_id #=> 70295696294220

Ruby array changes by changing a 'copy' of one of its elements

I'm trying to confirm whether my understanding is correct of these six lines of code:
string="this is a sentence"
words=string.split
first_word=words[0]
first_word[0]=first_word[0].upcase
out=words.join(" ")
puts(out)
which prints "This is a sentence" (with the first letter capitalized).
It would appear that changing the "first_word" string, which is defined as the first element of the "words" array, also changes the original "words" array. Is this indeed Ruby's default behavior? Does it not make it more difficult to track where in the code changes to the array take place?
You just need need to distinguish between a variable and an object. Your string is an object. first_word is a variable.
Look for example
a = "hello"
b = a
c = b
now all variables contain the same object, a string with the value "hello". We say they reference the object. No copy is made.
a[0] = 'H'
This changes the first character of the object, a string which now has the value "Hello". Both b and c contain the same, now changed object.
a = "different"
This assigns a new object to the variable a. b and c still hold the original object.
Is this Rubys default behaviour? yes. And it also works like this in many other programming languages.
Does it make it difficult to track changes? Sometimes.
If you takes an element from an array (like your first_word), you need to know:
If you change the object itself, no matter how you access it,
all variables will still hold your object, which just happened to be changed.
But if you replace the object in the array, like words[0] = "That", then all your other variables will still hold the original object.
This behavior is caused by how ruby does pass-by-value and pass-by-reference.
This is probably one of the more confusing parts of Ruby. It is well accepted that Ruby is a pass-by-value, high level programming language. Unfortunately, this is slightly incorrect, and you have found yourself a perfect example. Ruby does pass-by-value, however, most values in ruby are references. When Ruby does an assignment of a simple datatypes, integers, floats, strings, it will create a new object. However, when assigning objects such as arrays and hashes, you are creating references.
original_hash = {name: "schylar"}
reference_hash = original_hash
reference_hash[:name] = "!schylar"
original_hash #=> "!schylar"
original_array = [1,2]
reference_array = original_array
reference_array[0] = 3
reference_array #=> [3,2]
original_fixnum = 1
new_object_fixnum = original_fixnum
new_object_fixnum = 2
original_fixnum #=> 1
original_string = "Schylar"
new_object_string = original_string
new_object_string = "!Schylar"
original_string #=> "Schylar'
If you find yourself needing to copy by value, you may re-think the design. A common way to pass-by-value complex datatypes is using the Marshal methods.
a = {name: "Schylar"}
b = Marshal.load(Marshal.dump(a))
b[:name] = "!!!Schylar"
a #=> {:name => "Schylar"}

Are Hashes in Ruby passed by reference? [duplicate]

#user.update_languages(params[:language][:language1],
params[:language][:language2],
params[:language][:language3])
lang_errors = #user.errors
logger.debug "--------------------LANG_ERRORS----------101-------------"
+ lang_errors.full_messages.inspect
if params[:user]
#user.state = params[:user][:state]
success = success & #user.save
end
logger.debug "--------------------LANG_ERRORS-------------102----------"
+ lang_errors.full_messages.inspect
if lang_errors.full_messages.empty?
#user object adds errors to the lang_errors variable in the update_lanugages method.
when I perform a save on the #user object I lose the errors that were initially stored in the lang_errors variable.
Though what I am attempting to do would be more of a hack (which does not seem to be working). I would like to understand why the variable values are washed out. I understand pass by reference so I would like to know how the value can be held in that variable without being washed out.
The other answerers are all correct, but a friend asked me to explain this to him and what it really boils down to is how Ruby handles variables, so I thought I would share some simple pictures / explanations I wrote for him (apologies for the length and probably some oversimplification):
Q1: What happens when you assign a new variable str to a value of 'foo'?
str = 'foo'
str.object_id # => 2000
A: A label called str is created that points at the object 'foo', which for the state of this Ruby interpreter happens to be at memory location 2000.
Q2: What happens when you assign the existing variable str to a new object using =?
str = 'bar'.tap{|b| puts "bar: #{b.object_id}"} # bar: 2002
str.object_id # => 2002
A: The label str now points to a different object.
Q3: What happens when you assign a new variable = to str?
str2 = str
str2.object_id # => 2002
A: A new label called str2 is created that points at the same object as str.
Q4: What happens if the object referenced by str and str2 gets changed?
str2.replace 'baz'
str2 # => 'baz'
str # => 'baz'
str.object_id # => 2002
str2.object_id # => 2002
A: Both labels still point at the same object, but that object itself has mutated (its contents have changed to be something else).
How does this relate to the original question?
It's basically the same as what happens in Q3/Q4; the method gets its own private copy of the variable / label (str2) that gets passed in to it (str). It can't change which object the label str points to, but it can change the contents of the object that they both reference to contain else:
str = 'foo'
def mutate(str2)
puts "str2: #{str2.object_id}"
str2.replace 'bar'
str2 = 'baz'
puts "str2: #{str2.object_id}"
end
str.object_id # => 2004
mutate(str) # str2: 2004, str2: 2006
str # => "bar"
str.object_id # => 2004
In traditional terminology, Ruby is strictly pass-by-value. But that's not really what you're asking here.
Ruby doesn't have any concept of a pure, non-reference value, so you certainly can't pass one to a method. Variables are always references to objects. In order to get an object that won't change out from under you, you need to dup or clone the object you're passed, thus giving an object that nobody else has a reference to. (Even this isn't bulletproof, though — both of the standard cloning methods do a shallow copy, so the instance variables of the clone still point to the same objects that the originals did. If the objects referenced by the ivars mutate, that will still show up in the copy, since it's referencing the same objects.)
Ruby uses "pass by object reference"
(Using Python's terminology.)
To say Ruby uses "pass by value" or "pass by reference" isn't really descriptive enough to be helpful. I think as most people know it these days, that terminology ("value" vs "reference") comes from C++.
In C++, "pass by value" means the function gets a copy of the variable and any changes to the copy don't change the original. That's true for objects too. If you pass an object variable by value then the whole object (including all of its members) get copied and any changes to the members don't change those members on the original object. (It's different if you pass a pointer by value but Ruby doesn't have pointers anyway, AFAIK.)
class A {
public:
int x;
};
void inc(A arg) {
arg.x++;
printf("in inc: %d\n", arg.x); // => 6
}
void inc(A* arg) {
arg->x++;
printf("in inc: %d\n", arg->x); // => 1
}
int main() {
A a;
a.x = 5;
inc(a);
printf("in main: %d\n", a.x); // => 5
A* b = new A;
b->x = 0;
inc(b);
printf("in main: %d\n", b->x); // => 1
return 0;
}
Output:
in inc: 6
in main: 5
in inc: 1
in main: 1
In C++, "pass by reference" means the function gets access to the original variable. It can assign a whole new literal integer and the original variable will then have that value too.
void replace(A &arg) {
A newA;
newA.x = 10;
arg = newA;
printf("in replace: %d\n", arg.x);
}
int main() {
A a;
a.x = 5;
replace(a);
printf("in main: %d\n", a.x);
return 0;
}
Output:
in replace: 10
in main: 10
Ruby uses pass by value (in the C++ sense) if the argument is not an object. But in Ruby everything is an object, so there really is no pass by value in the C++ sense in Ruby.
In Ruby, "pass by object reference" (to use Python's terminology) is used:
Inside the function, any of the object's members can have new values assigned to them and these changes will persist after the function returns.*
Inside the function, assigning a whole new object to the variable causes the variable to stop referencing the old object. But after the function returns, the original variable will still reference the old object.
Therefore Ruby does not use "pass by reference" in the C++ sense. If it did, then assigning a new object to a variable inside a function would cause the old object to be forgotten after the function returned.
class A
attr_accessor :x
end
def inc(arg)
arg.x += 1
puts arg.x
end
def replace(arg)
arg = A.new
arg.x = 3
puts arg.x
end
a = A.new
a.x = 1
puts a.x # 1
inc a # 2
puts a.x # 2
replace a # 3
puts a.x # 2
puts ''
def inc_var(arg)
arg += 1
puts arg
end
b = 1 # Even integers are objects in Ruby
puts b # 1
inc_var b # 2
puts b # 1
Output:
1
2
2
3
2
1
2
1
* This is why, in Ruby, if you want to modify an object inside a function but forget those changes when the function returns, then you must explicitly make a copy of the object before making your temporary changes to the copy.
Is Ruby pass by reference or by value?
Ruby is pass-by-value. Always. No exceptions. No ifs. No buts.
Here is a simple program which demonstrates that fact:
def foo(bar)
bar = 'reference'
end
baz = 'value'
foo(baz)
puts "Ruby is pass-by-#{baz}"
# Ruby is pass-by-value
Ruby is pass-by-value in a strict sense, BUT the values are references.
This could be called "pass-reference-by-value". This article has the best explanation I have read: http://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/
Pass-reference-by-value could briefly be explained as follows:
A function receives a reference to (and will access) the same object in memory as used by the caller. However, it does not receive the box that the caller is storing this object in; as in pass-value-by-value, the function provides its own box and creates a new variable for itself.
The resulting behavior is actually a combination of the classical definitions of pass-by-reference and pass-by-value.
There are already some great answers, but I want to post the definition of a pair of authorities on the subject, but also hoping someone might explain what said authorities Matz (creator of Ruby) and David Flanagan meant in their excellent O'Reilly book, The Ruby Programming Language.
[from 3.8.1: Object References]
When you pass an object to a method in Ruby, it is an object reference that is passed to the method. It is not the object itself, and it is not a reference to the reference to the object. Another way to say this is that method arguments are passed by value rather than by reference, but that the values passed are object references.
Because object references are passed to methods, methods can use those references to modify the underlying object. These modifications are then visible when the method returns.
This all makes sense to me until that last paragraph, and especially that last sentence. This is at best misleading, and at worse confounding. How, in any way, could modifications to that passed-by-value reference change the underlying object?
Is Ruby pass by reference or by value?
Ruby is pass-by-reference. Always. No exceptions. No ifs. No buts.
Here is a simple program which demonstrates that fact:
def foo(bar)
bar.object_id
end
baz = 'value'
puts "#{baz.object_id} Ruby is pass-by-reference #{foo(baz)} because object_id's (memory addresses) are always the same ;)"
=> 2279146940 Ruby is pass-by-reference 2279146940 because object_id's (memory addresses) are always the same ;)
def bar(babar)
babar.replace("reference")
end
bar(baz)
puts "some people don't realize it's reference because local assignment can take precedence, but it's clearly pass-by-#{baz}"
=> some people don't realize it's reference because local assignment can take precedence, but it's clearly pass-by-reference
Parameters are a copy of the original reference. So, you can change values, but cannot change the original reference.
Try this:--
1.object_id
#=> 3
2.object_id
#=> 5
a = 1
#=> 1
a.object_id
#=> 3
b = 2
#=> 2
b.object_id
#=> 5
identifier a contains object_id 3 for value object 1 and identifier b contains object_id 5 for value object 2.
Now do this:--
a.object_id = 5
#=> error
a = b
#value(object_id) at b copies itself as value(object_id) at a. value object 2 has object_id 5
#=> 2
a.object_id
#=> 5
Now, a and b both contain same object_id 5 which refers to value object 2.
So, Ruby variable contains object_ids to refer to value objects.
Doing following also gives error:--
c
#=> error
but doing this won't give error:--
5.object_id
#=> 11
c = 5
#=> value object 5 provides return type for variable c and saves 5.object_id i.e. 11 at c
#=> 5
c.object_id
#=> 11
a = c.object_id
#=> object_id of c as a value object changes value at a
#=> 11
11.object_id
#=> 23
a.object_id == 11.object_id
#=> true
a
#=> Value at a
#=> 11
Here identifier a returns value object 11 whose object id is 23 i.e. object_id 23 is at identifier a, Now we see an example by using method.
def foo(arg)
p arg
p arg.object_id
end
#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23
arg in foo is assigned with return value of x.
It clearly shows that argument is passed by value 11, and value 11 being itself an object has unique object id 23.
Now see this also:--
def foo(arg)
p arg
p arg.object_id
arg = 12
p arg
p arg.object_id
end
#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23
#=> 12
#=> 25
x
#=> 11
x.object_id
#=> 23
Here, identifier arg first contains object_id 23 to refer 11 and after internal assignment with value object 12, it contains object_id 25. But it does not change value referenced by identifier x used in calling method.
Hence, Ruby is pass by value and Ruby variables do not contain values but do contain reference to value object.
It should be noted that you do not have to even use the "replace" method to change the value original value. If you assign one of the hash values for a hash, you are changing the original value.
def my_foo(a_hash)
a_hash["test"]="reference"
end;
hash = {"test"=>"value"}
my_foo(hash)
puts "Ruby is pass-by-#{hash["test"]}"
Two references refer to same object as long as there is no reassignment.
Any updates in the same object won't make the references to new memory since it still is in same memory.
Here are few examples :
a = "first string"
b = a
b.upcase!
=> FIRST STRING
a
=> FIRST STRING
b = "second string"
a
=> FIRST STRING
hash = {first_sub_hash: {first_key: "first_value"}}
first_sub_hash = hash[:first_sub_hash]
first_sub_hash[:second_key] = "second_value"
hash
=> {first_sub_hash: {first_key: "first_value", second_key: "second_value"}}
def change(first_sub_hash)
first_sub_hash[:third_key] = "third_value"
end
change(first_sub_hash)
hash
=> {first_sub_hash: {first_key: "first_value", second_key: "second_value", third_key: "third_value"}}
Ruby is interpreted. Variables are references to data, but not the data itself. This facilitates using the same variable for data of different types.
Assignment of lhs = rhs then copies the reference on the rhs, not the data. This differs in other languages, such as C, where assignment does a data copy to lhs from rhs.
So for the function call, the variable passed, say x, is indeed copied into a local variable in the function, but x is a reference. There will then be two copies of the reference, both referencing the same data. One will be in the caller, one in the function.
Assignment in the function would then copy a new reference to the function's version of x. After this the caller's version of x remains unchanged. It is still a reference to the original data.
In contrast, using the .replace method on x will cause ruby to do a data copy. If replace is used before any new assignments then indeed the caller will see the data change in its version also.
Similarly, as long as the original reference is in tact for the passed in variable, the instance variables will be the same that the caller sees. Within the framework of an object, the instance variables always have the most up to date reference values, whether those are provided by the caller or set in the function the class was passed in to.
The 'call by value' or 'call by reference' is muddled here because of confusion over '=' In compiled languages '=' is a data copy. Here in this interpreted language '=' is a reference copy. In the example you have the reference passed in followed by a reference copy though '=' that clobbers the original passed in reference, and then people talking about it as though '=' were a data copy.
To be consistent with definitions we must keep with '.replace' as it is a data copy. From the perspective of '.replace' we see that this is indeed pass by reference. Furthermore, if we walk through in the debugger, we see references being passed in, as variables are references.
However if we must keep '=' as a frame of reference, then indeed we do get to see the passed in data up until an assignment, and then we don't get to see it anymore after assignment while the caller's data remains unchanged. At a behavioral level this is pass by value as long as we don't consider the passed in value to be composite - as we won't be able to keep part of it while changing the other part in a single assignment (as that assignment changes the reference and the original goes out of scope). There will also be a wart, in that instance variables in objects will be references, as are all variables. Hence we will be forced to talk about passing 'references by value' and have to use related locutions.
Lots of great answers diving into the theory of how Ruby's "pass-reference-by-value" works. But I learn and understand everything much better by example. Hopefully, this will be helpful.
def foo(bar)
puts "bar (#{bar}) entering foo with object_id #{bar.object_id}"
bar = "reference"
puts "bar (#{bar}) leaving foo with object_id #{bar.object_id}"
end
bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"
# Output
bar (value) before foo with object_id 60
bar (value) entering foo with object_id 60
bar (reference) leaving foo with object_id 80 # <-----
bar (value) after foo with object_id 60 # <-----
As you can see when we entered the method, our bar was still pointing to the string "value". But then we assigned a string object "reference" to bar, which has a new object_id. In this case bar inside of foo, has a different scope, and whatever we passed inside the method, is no longer accessed by bar as we re-assigned it and point it to a new place in memory that holds String "reference".
Now consider this same method. The only difference is what with do inside the method
def foo(bar)
puts "bar (#{bar}) entering foo with object_id #{bar.object_id}"
bar.replace "reference"
puts "bar (#{bar}) leaving foo with object_id #{bar.object_id}"
end
bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"
# Output
bar (value) before foo with object_id 60
bar (value) entering foo with object_id 60
bar (reference) leaving foo with object_id 60 # <-----
bar (reference) after foo with object_id 60 # <-----
Notice the difference? What we did here was: we modified the contents of the String object, that variable was pointing to. The scope of bar is still different inside of the method.
So be careful how you treat the variable passed into methods. And if you modify passed-in variables-in-place (gsub!, replace, etc), then indicate so in the name of the method with a bang !, like so "def foo!"
P.S.:
It's important to keep in mind that the "bar"s inside and outside of foo, are "different" "bar". Their scope is different. Inside the method, you could rename "bar" to "club" and the result would be the same.
I often see variables re-used inside and outside of methods, and while it's fine, it takes away from the readability of the code and is a code smell IMHO. I highly recommend not to do what I did in my example above :) and rather do this
def foo(fiz)
puts "fiz (#{fiz}) entering foo with object_id #{fiz.object_id}"
fiz = "reference"
puts "fiz (#{fiz}) leaving foo with object_id #{fiz.object_id}"
end
bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"
# Output
bar (value) before foo with object_id 60
fiz (value) entering foo with object_id 60
fiz (reference) leaving foo with object_id 80
bar (value) after foo with object_id 60
Yes but ....
Ruby passes a reference to an object and since everything in ruby is an object, then you could say it's pass by reference.
I don't agree with the postings here claiming it's pass by value, that seems like pedantic, symantic games to me.
However, in effect it "hides" the behaviour because most of the operations ruby provides "out of the box" - for example string operations, produce a copy of the object:
> astringobject = "lowercase"
> bstringobject = astringobject.upcase
> # bstringobject is a new object created by String.upcase
> puts astringobject
lowercase
> puts bstringobject
LOWERCASE
This means that much of the time, the original object is left unchanged giving the appearance that ruby is "pass by value".
Of course when designing your own classes, an understanding of the details of this behaviour is important for both functional behaviour, memory efficiency and performance.

Why should a literal String start out Frozen? (Ruby 2.1)

Following development of Ruby 2.1 I have read about a feature that will probably be added so a developer is allowed to specify that a literal String should start out "frozen".
The syntax looks like this (note the trailing f):
str = "imfrozen"f # str receives a frozen string
In other Ruby documentation/wiki i've read that this feature provides the following benefit:
This allows the VM to use the same String object each time, and
potentially for the same frozen string across many files. It also
obviously provides all the immutability guarantees of a frozen string.
My questions are:
What is the benefit of this?
What is a real world example of when a feature like this would provide value?
How is this different from a symbol ?
Thank you
Suppose you had code like this
array_that_is_very_long.each do |e|
if e == "foo"
...
end
end
In this code, for each iteration over array_that_is_very_long, a new string "foo" is created (and is thrown out), which is a huge waste of resource. Currently, you can overcome this problem by doing:
Foo = "foo"
array_that_is_very_long.each do |e|
if e == Foo
...
end
end
The proposed syntax makes this easier to do as so:
array_that_is_very_long.each do |e|
if e == "foo"f
...
end
end

Strange Feature? of Ruby Arrays

I came around this strange feature(?) of arrays in Ruby and it would be very helpful if someone could explain to me why they work the way they do.
First lets give an example of how things usually work.
a = "Hello" #=> "Hello"
b = a #=> "Hello"
b += " Goodbye" #=> "Hello Goodbye"
b #=> "Hello Goodbye"
a #=> "Hello"
Ok cool, when you use = it creats a copy of the object (this time a string).
But when you use arrays this happens:
a = [1,2,3] #=> [1,2,3]
b = a #=> [1,2,3]
b[1] = 5 #=> [1,5,3]
b #=> [1,5,3]
a #=> [1,5,3]
Now thats just strange. Its the only object I've found that doesn't get copied when using = but instead just creates a refrance to the original object.
Can someone also explain (there must be a method) for copying an array without having it point back to the original object?
Actually, you should re-examine your premise.
The string assignment is really b = b + " Goodbye". The b + " Goodbye" operation returns an entirely new string, so the variable b is pointing to a new object after the assignment.
But when you assign to an individual array element, you are not creating an entirely new array, so a and b continue to point to the same object, which you just changed.
If you are looking for a rationale for the mutating vs functional behavior of arrays, it's simple. There is nothing to be gained by modifying the string. It is most likely necessary to allocate new memory anyway, so an entirely new string is created.
But an array can be arbitrarily large. Creating a new array in order to change just one element could be hugely expensive. And in any case, an array is like any other composite object. Changing an individual attribute does not necessarily affect any other attributes.
And to answer your question, you can always do:
b = a.dup
What happends there is that ruby is treating the Array object by reference and not by value.
So you can see it as this:
b= [1,2,3]
a= b
--'b' Points to---> [1,2,3] <--'a' points t---
So as you can see both point to the same reference, that means that if you change anything in a it will be reflected on b.
As for your question on the copying the object you could use the Object#clone method to do so.
Try your Array case with a String:
a = "Hello" #=> "Hello"
b = a #=> "Hello"
b[1] = "x" #=> "x"
b #=> "Hxllo"
a #=> "Hxllo"
Strings and Arrays work the same way in this regard.
The key difference in the two cases, as you wrote them, is this:
b += " Goodbye"
This is syntactic shorthand for
b = b + " Goodbye"
which is creating a new string from b + " Goodbye" and then assigning that to b. The way to modify an existing string, rather than creating a new one, is
b << " Goodbye"
And if you plug that into your sequence, you'll see that it modifies both a and b, since both variables refer to the same string object.
As for deep copying, there's a decent piece about it here:
http://ruby.about.com/od/advancedruby/a/deepcopy.htm

Resources