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

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

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 где-нибудь для ссылки на тот же экземпляр синглтона, однако люди обычно вместо этого пишут буквальное имя переменной, например HTTP_IF_MODIFIED_SINCE.

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

27
Weihang Jian 30 Дек 2014 в 12:14
5
Чтобы люди не мутировали их случайно?
 – 
Frederick Cheung
30 Дек 2014 в 12:20

3 ответа

Лучший ответ

Это правильно, что 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
37
Mike 23 Янв 2018 в 16:27
1
Спасибо за ваш ответ! Смешно, что я помню магический эффект String#freeze, но забываю первоначальное намерение #freeze...Orz
 – 
Weihang Jian
30 Дек 2014 в 12:40
4
Я считаю, что основная причина Rack and Rails связана с производительностью. См. эту статью Ричарда Шнеемана об оптимизации от используя String#freeze
 – 
Paul Hoffer
31 Дек 2014 в 08:12

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

log("debug")

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

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

log("debug".freeze)

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

От рубина 3.0 рубин заморозит каждую струну, - считает Мац.


обновление:

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

# frozen_string_literal: true

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

8
illusionist 8 Окт 2017 в 05:36

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

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

1
Eric Duminil 17 Фев 2018 в 01:00