В Ruby переменные — это просто ссылки на объект в памяти. Присваивая переменной значение, вы не самом деле ссылаетесь ей на объект. Присваивая переменной значение — другую переменную вы просто создаете две ссылки на один и тот же объект:
Одинаковый object_id — значит и переменные ссылаются на один и тот же объект:
Для создания нового объекта — копии используют два метода — #dup и #clone. Ниже преведены примеры использования обоих.
#dup создает shallow copy объекта. Что это узнаете ниже.
##А сейчас рассмотрим метод #clone:
#clone также создает shallow copy
##Что такое shallow copy?
Скажем так, существует 2 типа копий объекта: глубокие и мелкие копии. Shallow copy — в переводе означает «мелкая копия». Сейчас объясню почему.
#clone и #dup создают мелкие копии объектов, то есть копируют объект, но не составляющие его объекты. Если у нас есть массив строк, то делая его shallow copy мы получаем новый массив, но объекты — строки в нем будут те же. На практике это часто бывает бессмысленно, ведь действия выполненные над повторяющимися в обоих массивах элементами будут изменять элементы в обоих массивах, что небезопасно так как копирование объектов, чаще всего, нам необходимо в том случае, когда нам необходимо одновременно сохранить оригинальные данные и выполнить какие-то преобразования над ними. Используя shallow copy мы можем легко повредить оригинальные данные. Пример:
Как видите, оригинальные данные хранившиеся в переменной original были утеряны.
##Как быть, если необходима глубокая копия объекта?
В этом случае прибегают к трюку с использованием модуля Marshal. Marshal обладает 2 интересными методами: dump и load, первый (dump) позволяет закодировать объект в строку, чтобы потом, например, сохранить его в файл, а второй (load) позволяет восстановить объект из строки созданной методом dump. Ах да, эту строку называют дампом объекта, потому и название у метода такое — dump.
Выглядит эта техника следующим образом:
Пример попроще:
Как видите данная техника отлично работает и мы можем безопасно работать с данными.
При желании, можно добавить в класс Object метод #deep_copy, чтобы все было совсем «красиво»:
Однако мне это кажется лишним потому, что необходимость в таких действиях возникает достаточно редко.
На этом можно было бы закончить статью, но мы кое-что упустили. Что? — Различие между методами #dup и #clone. Эти методы, хоть и оба выполняют создание shallow copy объекта, работают несколько отлично, то есть #clone не является синонимом для #dup — это действительно 2 разных метода!
##Так в чем же разница?
Разница в следующем — #clone выполняет несколько более сложную работу, чем #dup, эта более сложная работа заключается в следующих двух особенностях #clone:
- #clone копирует singleton — класс объекта, то есть shallow-копие объекта доступны singleton методы скопированного объекта.
- #clone сохраняет frozen-статус копируемого объекта. Пример:
Вот и все! Удачи!