---
title: "Rubyのdupとcloneの違いと使い分け｜浅い・深いコピーとRailsのdeep_dupまで"
url: "https://www.issoh.co.jp/tech/details/2912/"
published: 2024-07-03
updated: 2026-07-19
categories: ["Ruby on Rails"]
publisher: "株式会社一創"
---

# Rubyのdupとcloneの違いと使い分け｜浅い・深いコピーとRailsのdeep\_dupまで

Rubyでオブジェクトを複製する`dup`と`clone`は、どちらも「浅いコピー」を返す点は同じですが、**frozen状態と特異メソッドを引き継ぐかどうか**で挙動が分かれます。この違いを取り違えると、コピーしたつもりの配列やハッシュが元の値まで書き換えてしまう不具合につながります。この記事では、dupとcloneの差、参照が共有される浅いコピーの落とし穴と深いコピーの作り方、配列・ハッシュの複製、そしてRails（ActiveRecord）で挙動が変わる`dup`／`clone`と`deep_dup`までを、実際のコードで整理します。

## まとめ：dupとcloneの使い分けと結論

- **dup**：浅いコピーを返す。frozen状態と特異メソッドは**引き継がない**。素直な複製が欲しいときの既定。
- **clone**：浅いコピーを返す。frozen状態と特異メソッドを**引き継ぐ**。Ruby 2.4以降は`clone(freeze: false)`で凍結を外せる。
- **浅いコピーの限界**：どちらもネストした配列・ハッシュの中身は**参照を共有**する。中身まで独立させたいなら深いコピー（`Marshal.load(Marshal.dump(obj))`）を使う。
- **Rails**：`ActiveRecord#dup`はidがnilの新規レコード、`#clone`は同じidを共有する。レコード複製には`dup`、ネストごと複製するなら`deep_dup`。

## dupメソッドとは｜オブジェクトの浅いコピーを作る

`dup`は、レシーバと同じクラスの新しいインスタンスを作り、インスタンス変数をコピーして返します。返るのは元とは別のオブジェクトで、`object_id`は一致しません。ただしコピーされるのは「そのオブジェクト自身」だけで、frozen状態や特異メソッドは持ち越しません。

```
original = { name: "Ruby", tags: ["lang"] }
copy = original.dup

copy.object_id == original.object_id  # => false（別オブジェクト）
copy[:name] = "Crystal"
original[:name]                        # => "Ruby"（トップレベルは独立）
```

凍結した文字列を`dup`すると、コピーは凍結が外れて再び変更できるようになります。「凍結済みの値をベースに、変更可能な複製が欲しい」ときの定番です。

```
frozen = "immutable".freeze
frozen.frozen?      # => true
frozen.dup.frozen?  # => false
```

## dupとcloneの違い｜frozen状態と特異メソッドの引き継ぎ

両者の分岐点は「オブジェクトに後付けした状態を引き継ぐか」です。`clone`はfrozen状態と特異メソッドまで含めて複製し、`dup`はそれらを落とします。

| 観点       | dup    | clone                     |
| -------- | ------ | ------------------------- |
| コピーの種類   | 浅いコピー  | 浅いコピー                     |
| インスタンス変数 | コピーする  | コピーする                     |
| frozen状態 | 引き継がない | 引き継ぐ                      |
| 特異メソッド   | 引き継がない | 引き継ぐ                      |
| 凍結の制御    | 常に非凍結  | freeze: true / false で指定可 |

特異メソッド（そのオブジェクトだけに定義したメソッド）を持たせた場合、`clone`は呼び出せますが`dup`は`NoMethodError`になります。

```
obj = Object.new
def obj.greet = "hello"

obj.clone.greet  # => "hello"
obj.dup.greet    # => NoMethodError
```

Ruby 2.4以降の`clone`はキーワード引数`freeze:`を取れます。`clone(freeze: false)`なら凍結オブジェクトから「特異メソッドを保ったまま変更可能な複製」を作れ、`clone(freeze: true)`なら元が非凍結でも凍結した複製を返します。

```
s = "abc".freeze
s.clone.frozen?                # => true（既定は元を踏襲）
s.clone(freeze: false).frozen? # => false
```

## 浅いコピーと深いコピー｜dup/cloneで参照が共有される落とし穴

`dup`も`clone`も浅いコピーです。トップレベルのオブジェクトは複製されますが、その中に入っている配列やハッシュ、文字列などの要素は**元と同じオブジェクトを指したまま**です。そのため、コピー側でネストした中身を破壊的に変更すると、元のオブジェクトにも影響します。

```
original = { list: [1, 2, 3] }
copy = original.dup

copy[:list] << 4
original[:list]  # => [1, 2, 3, 4]（中身の配列は共有されている）
```

中身まで完全に独立させたい場合は深いコピー（ディープコピー）が必要です。Ruby標準ライブラリだけで作るなら`Marshal`で一度シリアライズして復元する方法が確実です。

```
deep = Marshal.load(Marshal.dump(original))
deep[:list] << 99
original[:list]  # => [1, 2, 3]（影響しない）
```

ただし`Marshal`には制約があります。`Proc`やlambda、`IO`・ソケットなどダンプできないオブジェクトを含むと`TypeError`になり、特異メソッドも復元されません。

```
Marshal.dump(-> { })  # => TypeError: no _dump_data is defined for class Proc
```

こうしたオブジェクトを含む構造では、対象クラスに複製ロジックを自前で用意するか、後述のRailsの`deep_dup`を使います。

## 配列・ハッシュ・文字列を複製するときの実務

コレクションの複製でも「浅いコピー」であることは変わりません。配列やハッシュを`dup`しても、要素そのものは共有されます。

```
words = ["ab", "cd"]
copied = words.dup

copied << "ef"      # 配列の構造変更は元に影響しない
words              # => ["ab", "cd"]

copied[0].upcase!  # 要素の文字列は同じオブジェクト
words[0]           # => "AB"（元も変わる）
```

要素の破壊的変更まで避けたいなら、要素ごとに複製する（`words.map(&:dup)`）か、`Marshal`で深いコピーにします。凍結した配列を扱う場面では、`dup`は非凍結の複製を返し、`clone`は凍結を保ったまま複製する違いも押さえておくと、意図せず変更可能になった／できなかったという事故を防げます。文字列リテラルの凍結挙動はRubyのバージョンや`frozen_string_literal`マジックコメントに左右されるため、扱うRubyの仕様は[Ruby 4系の最新安定版の解説](https://www.issoh.co.jp/tech/details/10592/)もあわせて確認してください。

## RailsのActiveRecordとdeep\_dup｜dupが新規レコード・cloneが同idになる理由

Rails（ActiveRecord）では、モデルオブジェクトに対する`dup`と`clone`が標準Rubyと同じ発想でありながら、データベースの主キーが絡むぶん実務上の意味が大きく変わります。[Rails APIでレコードを扱う実装](https://www.issoh.co.jp/tech/details/2905/)でも、複製の選択を誤ると既存データを上書きしかねません。

| 観点               | ActiveRecord#dup | ActiveRecord#clone |
| ---------------- | ---------------- | ------------------ |
| id               | nil（新規レコード扱い）    | 元と同じidを共有          |
| 保存時の挙動           | 新しい行としてINSERT    | 既存の行をUPDATEし上書き    |
| タイムスタンプ          | コピーせず保存時に再設定     | 引き継ぐ               |
| 属性の独立性           | 複製側の変更は元に影響しない   | 属性ハッシュを共有し互いに影響しうる |
| 関連（associations） | 複製しない            | 複製しない              |

つまり「同じ内容の新しいレコードを作りたい」なら`dup`が正解です。`clone`はidを引き継ぐため、複製したつもりで`save`すると元のレコードを更新してしまいます。レコードの複製には原則`dup`を使い、`clone`は避けるのが安全です。

```
new_user = user.dup     # id=nil の新規レコード
new_user.new_record?    # => true
new_user.save           # 別レコードとしてINSERT
```

ネストした属性やハッシュ・配列カラムまで含めて独立に複製したいときは、ActiveSupportが提供する`deep_dup`を使います。`deep_dup`は配列・ハッシュを再帰的に複製し、要素の参照共有を断ち切ります。`Marshal`と違いProcを含んでいてもエラーにならず、Rails環境で扱いやすいのが利点です。

```
config = { roles: ["admin"], meta: { active: true } }
copied = config.deep_dup

copied[:roles] << "guest"
config[:roles]  # => ["admin"]（ネストも独立）
```

## dup・clone・deep copyの使い分けと失敗パターン

複製の手段は「どこまで独立させたいか」で選びます。ここまでの内容を判断基準に落とすと次のとおりです。

- **単純な複製が欲しい → dup**。frozenや特異メソッドを引きずらないぶん予測しやすく、既定はこれで十分。
- **frozen状態や特異メソッドまで保ちたい → clone**。凍結を外したいなら`clone(freeze: false)`。
- **ネストの中身まで独立させたい → 深いコピー**。Rails外は`Marshal`、Rails内は`deep_dup`。
- **ActiveRecordのレコード複製 → dup一択**。`clone`はidを共有し既存レコードを上書きする危険がある。

典型的な失敗パターンは3つに集約されます。第一に、浅いコピーで済ませてネストした配列・ハッシュを破壊的に変更し、元データまで壊すケース。第二に、凍結文字列を`clone`で複製して「なぜか変更できない」と詰まるケース（`dup`か`clone(freeze: false)`にする）。第三に、ActiveRecordで`clone`したレコードを`save`して既存行を上書きするケースです。いずれも「dup/cloneは浅いコピー」「cloneは状態を引き継ぐ」「AR#cloneはidを共有する」という3点を押さえれば避けられます。

## よくある質問

### dupとは何ですか？

オブジェクトの浅いコピーを作るRubyの標準メソッドです。同じクラスの新しいインスタンスを返し、インスタンス変数をコピーします。frozen状態や特異メソッドは引き継ぎません。

### dupとcloneはどちらを使うべきですか？

frozen状態や特異メソッドを保つ必要がなければ`dup`で十分です。凍結状態や後付けした特異メソッドまで含めて忠実に複製したい場合や、凍結の有無を明示的に制御したい場合に`clone`（必要なら`clone(freeze: false)`）を使います。

### Rubyでディープコピー（深いコピー）を作るには？

標準ライブラリだけなら`Marshal.load(Marshal.dump(obj))`で、ネストした中身まで独立した複製を作れます。ただしProcやIOを含むオブジェクトはダンプできず`TypeError`になります。Rails環境ではActiveSupportの`deep_dup`が使えます。

### 配列やハッシュをdupしても中身が一緒に変わるのはなぜですか？

`dup`は浅いコピーで、配列やハッシュの要素は元と同じオブジェクトを参照したままだからです。要素の破壊的変更まで避けたい場合は、要素ごとに複製するか深いコピーを使います。

### Railsでレコードを複製するときdupとcloneのどちらが安全ですか？

`dup`です。`dup`はidがnilの新規レコードを返すため、保存すると別の行として作成されます。`clone`は同じidを共有し、保存すると既存レコードを上書きするため、複製目的には向きません。

## 関連記事

- [Rails APIとは？作成手順・JWT認証・React連携・Docker環境を実装解説【Rails 8.1】](https://www.issoh.co.jp/tech/details/2905/)
- [ruby on rails を vscode で開発する環境構築【2026年版】拡張機能・デバッグ・Dev Container](https://www.issoh.co.jp/tech/details/2878/)
- [Ruby 4.0.1がリリースされました – 新機能満載のRuby 4系に初の安定版パッチが登場しました](https://www.issoh.co.jp/tech/details/10592/)
- [rbenvとは何か？RVMとの違いや特徴を徹底解説する基本ガイド](https://www.issoh.co.jp/tech/details/7474/)

---

出典: [Rubyのdupとcloneの違いと使い分け｜浅い・深いコピーとRailsのdeep\_dupまで](<https://www.issoh.co.jp/tech/details/2912/>)（株式会社一創）
