I have a class where I was using the Array#shift
instance method on an instance variable. I thought I made a \"copy\" of my instance variable but in fact I hadn
The preferred method is dup
array.dup
whenever you need to copy an arrayarray.map(&:dup)
whenever you need to copy a 2D arrayDon't use the marshalling trick unless you really want to deep copy an entire object graph. Usually you want to copy the arrays only but not the contained elements.
This is the tricky concept of mutability in ruby. In terms of core objects, this usually comes up with arrays and hashes. Strings are mutable as well, but this can be disabled with a flag at the top of the script. See What does the comment "frozen_string_literal: true" do?.
In this case, you can call dup
, deep_dup
, clone
easily to the same effect as replace
:
['some', 'array'].dup
['some', 'array'].deep_dup
['some', 'array'].clone
Marshal.load Marshal::dump(['some', 'array'])
In terms of differences, dup
and clone
are the same except for some nuanced details - see What's the difference between Ruby's dup and clone methods?
The difference between these and deep_dup
is that deep_dup
works recursively. For example if you dup a nested array, the inner array will not be cloned:
a = [[1]]
b = a.clone
b[0][0] = 2
a # => [[2]]
The same thing happens with hashes.
Marshal.load Marshal::dump <object>
is a general approach to deep cloning objects, which, unlike deep_dup
, is in ruby core. Marshal::dump
returns a string so it can be handy in serializing objects to file.
If you want to avoid unexpected errors like this, keep a mental index of which methods have side-effects and only call those when it makes sense to. An explanation point at the end of a method name indicates that it has side effects, but others include unshift, push, concat, delete, and pop. A big part of fuctional programming is avoiding side effects. You can see https://www.sitepoint.com/functional-programming-techniques-with-ruby-part-i/