Imagine this situation:

a = [1, 2, 3 ]
b  = a

b.delete(2)

b
#=> [1, 3]

a
#=> [1, 3]

Similar dangerous situation happens when you add elements to Array:

c = [1, 2, 3 ]
d  = c

d << 4

d
#=> [1, 2, 3, 4]

c
#=> [1, 2, 3, 4

This is not really a issue. It’s how Ruby works.

Explanation

In Ruby lang variables are assigned as a reference - they point to same object (source)

a = []
a  --------->  object

Therefore a is just reference to object in computer memory

When you do

a = b

You really set in b the same reference as a is pointing to

a  ------\
          >----> object
b  ------/

It’s not just Array

This doesn’t really have anything to do with Array, specifically. Ruby assigns by reference, so any method call that changes its receiver in-place has the potential to manifest this behavior.

Hash example:

x = {id: 1, name: 'allisio', skill: 'pro' }
y = x
y.delete(:id)

y
# => {:name=>"allisio", :skill=>"pro"}

x
# => {:name=>"allisio", :skill=>"pro"}

y[:lang] = 'ruby'

y
# => {:name=>"allisio", :skill=>"pro", :lang=>"ruby"}

y
# => {:name=>"allisio", :skill=>"pro", :lang=>"ruby"}

String example:

a = 'abcd'
b = a

b.gsub!('ab', 'xx')

b
# => 'xxcd'

a
# => 'xxcd'

Custom object example:

class Foo
  attr_accessor :value
end

foo = Foo.new
foo.value = 1

bar = foo
bar.value =2

bar.value
# => 2

foo.value
# => 2

Solution

Here are few options how to tell Ruby to reference a new object

Clone the object

a = [1,2,3]
b = a.dup

b.delete(2)

b
# => [1, 3]

a
# => [1, 2, 3]

Do functional calculation

There is famous statement in Functional programming world: “State is root of all evil”

You can performing functional operation with the Array a that results in returning new array:

a = [1,2,3]
b = a - [2]

b
# => [1, 3]

a
# => [1, 2, 3]

Same for other objects

a = 'abcd'
b = a.gsub('ab', 'xx')

a
# => 'abcd'

b
# => 'xxcd'

Freeze your Object

In order to prevent your Junior developers to do this mistake you may want to freeze the Array

a = [1,2,3].freeze
b = a

b.delete(2)
# FrozenError (can't modify frozen Array)

b
# => [1,2,3]

a
# => [1,2,3]

Special Thanks

In first version of the article I’ve described the problem from wrong perspective

Thank you allisio for the help on describing the problem correctly

Here are some article that goes deeper into this topic:

Similar topics:

Discussion