Why are there frozen constants everywhere?

Weihang Jian picture Weihang Jian · Dec 30, 2014 · Viewed 7.3k times · Source

We can easily find such style from lots of famous repositories, like rack, rails, etc.

For example in rack:

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

Another examle in 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

I wonder why these constant strings are frozen. Since they are all constants, there should be only one instance. Of course we can put "foo".freeze somewhere to reference the same singleton instance, however people usually write literal variable name like HTTP_IF_MODIFIED_SINCEinstead.

So in my opinion, it doesn't make any difference in spite of using #freeze, so why do people freeze constants?

Answer

spickermann picture spickermann · Dec 30, 2014

It is correct that Ruby prints a warning when you re-assign a value to an already initialized constant:

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

But there is no protection from changing the value in the constant. Example without freeze:

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

But freeze allows to protect the value of the constants from being changed. Example with freeze:

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