Help us understand the problem. What is going on with this article?

RubyのHashとArrayをdeep freezeするfreeezerというgemを書きました

More than 1 year has passed since last update.

RubyでHashとArrayをdeep freezeするための, freeezerというgemを書きました.

そもそもRubyのfreezeとは?

Rubyのfreezeはオブジェクトへの破壊的な変更を防ぐためのメソッドで, よく定数の定義とセットで使われます.

Rubyはデフォルトで定数への破壊的変更を許可しているため, 例えば以下のような現象が起こりえます.

class Test
  SOME_CONST = 'some const'
end

Test::SOME_CONST.upcase!
Test::SOME_CONST # => 'SOME CONST'

これを防ぐために,定数にイミュータブルなオブジェクトを代入する場合は freeze して破壊的変更を禁止する場合があるわけです.

class Test
  SOME_CONST = 'some const'.freeze
end

Test::SOME_CONST.upcase!
Test::SOME_CONST # => raise RuntimeError: can't modify frozen String

ArrayやHashオブジェクトの場合は中身までfreezeする必要がある

Stringをfreezeしたい場合などは上記の通り単純にfreezeすればいいのですが, ArrayやHashオブジェクトをfreezeしたい場合にはもう少し注意になります.

例えば以下のようにArrayをfreezeしてもArrayの参照しているオブジェクトまではfreezeされません.

SOME_ARRAY = ['one', 'two', 'three'].freeze

# Arrayへの破壊的変更は禁止される
SOME_ARRAY.push('four') # => raise RuntimeError: can't modify frozen Array

# Arrayが参照しているオブジェクトへの破壊的変更は許可される
SOME_ARRAY[0].upcase!
SOME_ARRAY # => ['ONE', 'two', 'three']

もし本当にArrayオブジェクトの参照しているオブジェクトまでイミュータブルにしたければ,以下のように各オブジェクトもfreezeする必要があります.

SOME_ARRAY = ['one'.freeze, 'two'.freeze, 'three'.freeze].freeze
SOME_ARRAY[0].upcase! # => raise RuntimeError: can't modify frozen String

というわけでこれを一発でできるのがfreeezerというgem

壮大な前フリになってしまいましたが, 要するにArrayとHashの中身まで一発でイミュータブルにできるのがfreeezerというgemです.

using Freeezer
SOME_ARRAY = ['one', 'two', 'three'].deep_freeze

SOME_ARRAY.push('four') # => raise RuntimeError: can't modify frozen Array
SOME_ARRAY[0].upcase! # => raise RuntimeError: can't modify frozen String

上記のように using Freezer を呼んでもらえれば deep_freeze ※1 というメソッド一発で再帰的に中のオブジェクトまでfreezeしてくれます.

所感など

自分で書いておいてなんですが, 正直クラスにある1つか2つの定数をfreezeするためにこのgemを入れて using Freeeze って書くかと言われるとちょっと微妙かなーという気はしています.

あとたぶん定数でStringのArrayとかHashを使いたい場合がStringじゃなくてSymbolで定義すればいいのではという気もしています(Symbolはイミュータブル).

なのでもし使うとしたら Constants みたいな定数をまとめて定義しているファイルがプロジェクトにあって,そこで何度も中身をfreezeする処理を書く場合にはいいのかもしれません.

また,今回の deep_freeze の元ネタはプロを目指すためのRuby入門 の8章 "モジュールを理解する" で例題として紹介されていた deep_freeze です. こいつをもう少し真面目に書いたらどうなるかということをやってみたくてgemを作ってみました.

※1 本当は deep_freeze じゃなくて freeeze にしたかったんですが, 流石に怒られそうだし, deep_freeze のほうが意味が伝わりやすいので断念しました.

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした