FrozenError: can't modify frozen String の原因と直し方
freezeしたオブジェクトを破壊的メソッド(<<やupcase!など)で変更しようとすると、FrozenErrorになります。定数として扱いたい文字列にfreezeを付けると、以後は書き換えが禁止されます。
エラーメッセージの読み方
main.rb:2:in `<main>': can't modify frozen String: "Alice" (FrozenError)
main.rb- ファイル名
2- 行番号 — 実際にクラッシュした行
<main>- 発生場所 — <main> ならトップレベル、メソッド名ならそのメソッドの中
FrozenError- 例外クラス — 何が起きたか。ここを検索するのが最短です
can't modify frozen String: "Alice"- 詳細メッセージ — どの値が問題だったか
このエラーが出る典型パターン
パターン1
1 name = "Alice".freeze 2 puts(name << " Smith") ^
main.rb:2:in `<main>': can't modify frozen String: "Alice" (FrozenError)
<<は文字列そのものを書き換える破壊的メソッドです。freezeした文字列には使えないため、新しい文字列を作る+に置き換える必要があります。
直し方: << を + にします。
広告
広告スロット(未設定)
パターン2
1 title = "Manager".freeze 2 puts(title << "!") ^
main.rb:2:in `<main>': can't modify frozen String: "Manager" (FrozenError)
定数のように扱いたい文字列にfreezeを付けると、以後は破壊的メソッドによる変更が一律で禁止されます。
直し方: << を + にします。
パターン3
1 code = "AB".freeze 2 puts(code << "12") ^
main.rb:2:in `<main>': can't modify frozen String: "AB" (FrozenError)
freezeは変数への再代入ではなく、オブジェクトそのものを凍結します。中身を変える操作はすべて拒否されます。
直し方: << を + にします。
パターン4
1 tag = "ruby".freeze 2 puts(tag << "-lang") ^
main.rb:2:in `<main>': can't modify frozen String: "ruby" (FrozenError)
+は元の文字列を変えずに新しい文字列を作るので、freezeされたオブジェクトにも安全に使えます。
直し方: << を + にします。
パターン5
1 prefix = "Mr.".freeze 2 puts(prefix << " Smith") ^
main.rb:2:in `<main>': can't modify frozen String: "Mr." (FrozenError)
破壊的メソッド(!や<<が付くもの)を使う前に、レシーバがfreezeされていないか意識する必要があります。
直し方: << を + にします。
よくある誤解
「freezeは変数への再代入を禁止するだけのはず」という思い込みは誤りです。freezeはオブジェクトそのものを凍結するため、破壊的メソッドによる中身の変更も一律で禁止されます。
まとめ
FrozenError: can't modify frozen Stringは中級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。