LoginSignup
6
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-02-25

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 のほうが意味が伝わりやすいので断念しました.

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2