Rubyでのオブジェクトコピー dupとcloneについて
みなさんこんにちは
意識高い系エンジニアの僕です。
最近Rubyでのオブジェクトコピーについていくつか質問されたので、まずは初歩的なところをまとめてみました。
dupとclone
Rubyでオブジェクトをコピーする場合、Object#dup
やObject#clone
が使えますが、
これらは以下のように挙動が違います。
メソッド名 | 凍結 | 汚染 | 信頼 | 特異 |
---|---|---|---|---|
dup | ☓ | ◯ | ◯ | ☓ |
clone | ◯ | ◯ | ◯ | ◯ |
− clone
メソッドはオブジェクトの凍結状態、汚染状態、信頼状態をコピーする。
− clone
メソッドはオブジェクトの特異メソッドをコピーする。
− dup
メソッドはオブジェクトの汚染状態、信頼状態をコピーする。
− dup
メソッドはオブジェクトの特異メソッドをコピーしない。
シャローコピーとディープコピー
シャローコピー
clone
もdup
も、ともにシャローコピー(浅いコピーのため)、オブジェクトの参照先
まではコピーしません。
class Dog attr_accessor :name, :age def initialize(name: nil, age: nil) self.name = name self.age = age end end dog1 = Dog.new(name: 'pochi', age: 10) dog2 = dog1.dup dog2.name.gsub!(/po/, 'ha') p dog1.name p dog2.name
ruby duptest.rb "hachi" "hachi"
この例ではdog1からcloneしたdog2のname
プロパティの値を変更すると、dog1のname
プロパティの値も変更されています。
これは指し示しているオブジェクトが同じだからです。
class Dog attr_accessor :name, :age def initialize(name: nil, age: nil) self.name = name self.age = age end end dog1 = Dog.new(name: 'pochi', age: 10) dog2 = dog1.dup p dog1.name.object_id p dog2.name.object_id
ruby duptest.rb 70213018418320 70213018418320
name
プロパティに格納されている文字列オブジェクトのobject_idが同じなのがわかります。
ディープコピー
オブジェクトの参照先までコピーする場合、RubyではMarshal.dump
とMarshal.load
を使うことで実現できます。
class Dog attr_accessor :name, :age def initialize(name: nil, age: nil) self.name = name self.age = age end end dog1 = Dog.new(name: 'pochi', age: 10) dog2 = Marshal.load(Marshal.dump(dog1)) dog2.name.gsub!(/po/, 'ha') p dog1.name p dog2.name
ruby duptest.rb "pochi" "hachi"
この例ではdog1からMarshal.dump
とMarshal.load
でコピーしたdog2の
name
プロパティの値を変更しても、dog1のname
は変更されません。
これは、Marshal.dump
とMarshal.load
でコピーした場合は、name
が
指し示している文字列オブジェクトが新しく生成されて格納されたためです。
class Dog attr_accessor :name, :age def initialize(name: nil, age: nil) self.name = name self.age = age end end dog1 = Dog.new(name: 'pochi', age: 10) dog2 = Marshal.load(Marshal.dump(dog1)) dog2.name.gsub!(/po/, 'ha') p dog1.name.object_id p dog2.name.object_id
ruby duptest.rb 70297823591600 70297823591380
name
プロパティに格納されている文字列オブジェクトのobject_idが違うのがわかります。
Marshalモジュールとは
Marshalモジュールは、Rubyオブジェクトをファイル(または文字列)に書き出したり、
読み戻したりする機能を提供するモジュールです。
Marshal.dump
はオブジェクトをファイルに書き出すメソッドで、Marshal.dump
は
書き出したファイルを読み込んでオブジェクトを生成するメソッドです。
Rubyでディープコピーをする方法として、この2つのメソッドを使う方法がよく紹介されていますね。
Marshalモジュールを使う場合の注意点
Marshalでファイルに書き出せない以下の様なオブジェクトをMarshalしようとすると、TypeErrorが発生します。
- 名前のついてない Class/Module オブジェクト
- システムがオブジェクトの状態を保持するもの。具体的には以下のイン スタンス。Dir, File::Stat, IO とそのサブクラス File, Socket など
- MatchData, Data, Method, UnboundMethod, Proc, Thread, ThreadGroup, Continuation のインスタンス。
- 特異メソッドを定義したオブジェクト
次回はRailsでのオブジェクトコピーについてまとめてみようと思います。
TAG
新しいことや難しい課題に挑戦することにやりがいを感じ、安定やぬるい事は退屈だと感じます。 考えるより先に手が動く、肉体派エンジニアで座右の銘は諸行無常。 大事なのは感性、プログラミングにおいても感覚で理解し、感覚で書きます。
TAG
- Android
- AWS
- Bitrise
- CodePipeline
- Firebase
- HTML
- iOS
- IoT
- JavaScript
- KPI
- Linux
- Mac
- Memcached
- MGRe
- MGReのゆるガチエンジニアブログ
- MySQL
- PHP
- PICK UP
- PR
- Python
- Ruby
- Ruby on Rails
- SEO
- Swift
- TIPS
- UI/UX
- VirtualBox
- Wantedly
- Windows
- アクセス解析
- イベントレポート
- エンジニアブログ
- ガジェット
- カスタマーサクセス
- サーバ技術
- サービス
- セキュリティ
- セミナー・展示会
- テクノロジー
- デザイン
- プレスリリース
- マーケティング施策
- マネジメント
- ラボ
- リーンスタートアップ
- 企画
- 会社紹介
- 会社紹介資料
- 勉強会
- 実績紹介
- 拡張性
- 採用
- 日常
- 書籍紹介
- 歓迎会
- 社内イベント
- 社員インタビュー
- 社長ブログ
- 視察
- 開発環境