Почему везде есть замороженные константы?
Мы можем легко найти такой стиль из множества известных репозиториев, таких как стойки, рельсы и т.д.
Например, в стойке:
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.