Почему везде есть замороженные константы?

Мы можем легко найти такой стиль из множества известных репозиториев, таких как стойки, рельсы и т.д.

Например, в стойке:

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

Еще один пример в рельсах:

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

Интересно, почему эти постоянные строки заморожены. Поскольку все они являются константами, должен быть только один экземпляр. Конечно, мы можем помещать "foo".freeze где-то, чтобы ссылаться на один и тот же экземпляр singleton, однако люди обычно пишут буквальное имя переменной, например HTTP_IF_MODIFIED_SINCE.

Поэтому, на мой взгляд, это не имеет никакого значения, несмотря на использование #freeze, так почему люди замораживают константы?

Ответ 1

Правильно, что Ruby выводит предупреждение, когда вы повторно назначаете значение уже инициализированной константе:

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

Но нет никакой защиты от изменения значения в константе. Пример без freeze:

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

Но freeze позволяет защитить значение констант от изменения. Пример с freeze:

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

Ответ 2

обычно Rubyist замораживает строковые литералы, чтобы сделать выполнение быстрее. Если в некотором контроллере есть какой-то вызов функции, например, как показано ниже, каждый запрос вызовет эту функцию.

log("debug")

что происходит, Ruby каждый раз определяет новый объект строкой мусора. Размещение объектов не является бесплатным. он потребляет память и процессор. Мусор будет там, пока GC их не соберет.

но если литералы заморожены

log("debug".freeze)

ruby выделяет один раз и кэширует его для последующего использования. Кроме того, строковый объект будет неизменным и безопасным для использования в многопоточной среде.

От рубина 3,0 рубина замерзнет каждую строку, - согласно Мацу.


Обновить:

Если вы добавите следующий комментарий в начале файла ruby, то любой строковый литерал во всем файле будет неизменным. Это очень полезно, когда вы пытаетесь оптимизировать свое приложение для среды с muti-threaded.

# frozen_string_literal: true

или вы даже можете запустить свой Ruby-процесс с помощью переключателя --enable-frozen-string-literal.

Ответ 3

Одно из объяснений, почему вы видите это постоянное замораживание констант в популярных проектах, заключается в том, что они используют анализатор кода Rubocop.

Это стандартное правило Rubocop, что константы не должны изменяться по причинам, указанным выше, by @spickermann.