Techioz Blog

なぜあちこちに固定された定数があるのでしょうか?

概要

ラックやレールなど、多くの有名なリポジトリからそのようなスタイルを簡単に見つけることができます。

たとえばラック内では次のようになります。

PATH_INFO      = 'PATH_INFO'.freeze
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
SCRIPT_NAME    = 'SCRIPT_NAME'.freeze
QUERY_STRING   = 'QUERY_STRING'.freeze
CACHE_CONTROL  = 'Cache-Control'.freeze
CONTENT_LENGTH = 'Content-Length'.freeze
CONTENT_TYPE   = 'Content-Type'.freeze

Rails での別の例:

HTTP_IF_MODIFIED_SINCE = 'HTTP_IF_MODIFIED_SINCE'.freeze
HTTP_IF_NONE_MATCH     = 'HTTP_IF_NONE_MATCH'.freeze
HTTP_IF_NONE_MATCH     = 'HTTP_IF_NONE_MATCH'.freeze

なぜこれらの定数文字列が凍結されているのか不思議です。これらはすべて定数であるため、インスタンスは 1 つだけ存在する必要があります。もちろん、同じシングルトン インスタンスを参照するために “foo”.freeze をどこかに置くこともできますが、通常は代わりに HTTP_IF_MODIFIED_SINCE のようなリテラル変数名を書きます。

私の意見では、#freeze を使用しても何の違いもありません。では、なぜ人々は定数を凍結するのでしょうか?

解決策

既に初期化された定数に値を再代入すると、Ruby が警告を出力するのは正しいです。

FOO = 'foo'
FOO = 'bar'
# :2: warning: already initialized constant FOO
# :1: warning: previous definition of FOO was here
FOO
#=> "bar"

ただし、定数の値の一部を変更することに対する保護はありません。フリーズなしの例:

FOO = 'foo'
FOO[1] = '-'
FOO
#=> "f-o"

ただし、フリーズすると、定数の値が変更されないように保護できます。フリーズを使用した例:

FOO = 'foo'.freeze
FOO[1] = '-'
#=> RuntimeError: can't modify frozen String