dup はfreeze状態を引き継がない(clone との違い)
freezeしたオブジェクトをdupで複製すると、複製後のオブジェクトはfreezeされていない通常の状態になります。freeze状態も含めて複製したい場合はcloneを使う必要があります。
エラーメッセージの読み方
main.rb:3:in `<main>': can't modify frozen String: "secret" (FrozenError)
main.rb- ファイル名
3- 行番号 — 実際にクラッシュした行
<main>- 発生場所 — <main> ならトップレベル、メソッド名ならそのメソッドの中
FrozenError- 例外クラス — 何が起きたか。ここを検索するのが最短です
can't modify frozen String: "secret"- 詳細メッセージ — どの値が問題だったか
このエラーが出る典型パターン
パターン1
1 original = "secret".freeze 2 copy = original.clone ^ 3 copy << "!" 4 puts copy
main.rb:3:in `<main>': can't modify frozen String: "secret" (FrozenError)
cloneはfreeze状態も含めて複製するため、複製後も凍結されたままです。中身を変更したいならdupを使う必要があります。
直し方: clone を dup にします。
広告
広告スロット(未設定)
パターン2
1 original = "token".freeze 2 copy = original.clone ^ 3 copy << "-x" 4 puts copy
main.rb:3:in `<main>': can't modify frozen String: "token" (FrozenError)
「複製すれば元の状態がすべて引き継がれるはず」という思い込みは、freeze状態に関してはcloneにだけ当てはまります。
直し方: clone を dup にします。
パターン3
1 original = "pin".freeze 2 copy = original.clone ^ 3 copy << "9" 4 puts copy
main.rb:3:in `<main>': can't modify frozen String: "pin" (FrozenError)
dupは中身だけを複製し、凍結状態はリセットされた新しいオブジェクトになります。
直し方: clone を dup にします。
パターン4
1 original = "key".freeze 2 copy = original.clone ^ 3 copy << "-2" 4 puts copy
main.rb:3:in `<main>': can't modify frozen String: "key" (FrozenError)
複製したオブジェクトを書き換えたいという目的がはっきりしているなら、cloneではなくdupを選びます。
直し方: clone を dup にします。
パターン5
1 original = "code".freeze 2 copy = original.clone ^ 3 copy << "-a" 4 puts copy
main.rb:3:in `<main>': can't modify frozen String: "code" (FrozenError)
dupとcloneの違いはfreeze状態の扱いだけです。それ以外の複製結果は同じです。
直し方: clone を dup にします。
よくある誤解
「複製すれば元のオブジェクトの状態はすべて引き継がれるはず」という思い込みは、freeze状態に関してはdupには当てはまりません。dupは中身だけを複製し凍結状態はリセットしますが、cloneは凍結状態も含めて複製します。
まとめ
dup はfreeze状態を引き継がない(clone との違い)は上級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。