Что делает комментарий «frozen_string_literal: true»?

226

Это rspecbinstub в моем каталоге проекта.

#!/usr/bin/env ruby
begin
  load File.expand_path("../spring", __FILE__)
rescue LoadError
end
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'rspec' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
  Pathname.new(__FILE__).realpath)

require "rubygems"
require "bundler/setup"

load Gem.bin_path("rspec-core", "rspec")

Что это должно сделать?

# frozen_string_literal: true
messanjah
источник

Ответы:

314

# frozen_string_literal: trueэто магический комментарий, впервые поддерживаемый в Ruby 2.3, который сообщает Ruby, что все строковые литералы в файле неявно заморожены, как если бы #freezeони были вызваны для каждого из них. То есть, если строковый литерал определен в файле с этим комментарием, и вы вызываете метод для этой строки, который изменяет его, например <<, вы получитеRuntimeError: can't modify frozen String .

Комментарий должен быть в первой строке файла.

В Ruby 2.3 этот магический комментарий можно использовать для подготовки к фиксированным строковым литералам, которые по умолчанию используются в Ruby 3 .

В Ruby 2.3 запускается с --enable=frozen-string-literalфлагом, а в Ruby 3 строковые литералы фиксируются во всех файлах. Вы можете переопределить глобальные настройки с помощью # frozen_string_literal: false.

Если вы хотите, чтобы строковый литерал был изменяемым независимо от глобальных или файловых настроек, вы можете поставить перед ним префиксный унарный +оператор (будьте осторожны с приоритетом оператора) или вызвать .dupего:

# frozen_string_literal: true
"".frozen?
=> true
(+"").frozen?
=> false
"".dup.frozen?
=> false

Вы также можете заморозить изменяемую (незамерзшую) строку с помощью одинарного кода -.

Дейв Швейсгут
источник
25
Что касается замораживания строк, важно отметить, что это улучшает производительность приложения . Смотрите также здесь
Andres
2
@ dave-schweisguth Разве мы не должны ожидать -"foo"того же "foo".freeze? Когда я проверяю, (-"foo").__id__я каждый раз получаю другое значение, но "foo".freeze.__id__оно всегда одинаково. Любые идеи?
Лилол
Интересно, если эта функция является проблемой, кажется, она вызывается только с унарным минусом. github.com/ruby/ruby/blob/trunk/string.c#L2572
лило
2
-предназначен для дедупликации строки для экономии памяти, в дополнение к возврату замороженной строки.
Эрегон
9
Хотя вы все еще можете использовать магический комментарий, Мэтц официально решил не делать все строковые литералы неизменяемыми по умолчанию в Ruby 3: bugs.ruby-lang.org/issues/11473#note-53
Константин Тихонов
44

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

Несколько очевидных наблюдений, которые нужно иметь в виду.

1. Замораживая строковые литералы, вы не выделяете для этого новое пространство памяти.

Пример:

Без волшебного комментария выделяется новое пространство для той же строки (обратите внимание на разные идентификаторы объекта)

def hello_id
  a = 'hello'
  a.object_id
end

puts hello_id   #=> 70244568358640
puts hello_id   #=> 70244568358500

С волшебным комментарием , рубин выделяет место только один раз

# frozen_string_literal: true

def hello_id
  a = 'hello'
  a.object_id
end

puts hello_id   #=> 70244568358640
puts hello_id   #=> 70244568358640

2. Замораживая строковые литералы, ваша программа вызовет исключение при попытке изменить строковый литерал.

Пример:

Без магического комментария вы можете изменить строковые литералы.

name = 'Johny'
name << ' Cash'

puts name     #=> Johny Cash

С волшебным комментарием , будет возникать исключение, когда вы изменяете строковые литералы

# frozen_string_literal: true

name = 'john'
name << ' cash'  #=> `<main>': can't modify frozen String (FrozenError)

puts name      

Всегда есть чему поучиться и быть гибким:

imechemi
источник
Это более интуитивный ответ.
Джин Лим
20

В Ruby 3.0. Matz (создатель Ruby) решил сделать все строковые литералы замороженными по умолчанию.

Вы можете использовать в Ruby 2.x. Просто добавьте этот комментарий в первую строку ваших файлов.

# frozen_string_literal: true

Приведенный выше комментарий в верхней части файла меняет семантику статических строковых литералов в файле. Статические строковые литералы будут заморожены и всегда будут возвращать один и тот же объект. (Семантика динамических строковых литералов не изменяется.)

Этот способ имеет следующие преимущества:

Нет уродливого f-суффикса. Нет ошибки синтаксиса на старых Ruby. Нам нужна только строка для каждого файла.

Пожалуйста, прочитайте эту тему для получения дополнительной информации.

https://bugs.ruby-lang.org/issues/8976

Александр
источник
К сожалению, этот комментарий не работает для строк в массивах, поэтому они все еще должны быть явно
заморожены
3
К сожалению это не будет в ruby ​​3 bugs.ruby-lang.org/issues/11473#note-53
zhisme