¿Qué hace el comentario "frozen_string_literal: true"?

226

Este es el rspecbinstub en mi directorio de proyectos.

#!/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")

¿Qué se pretende hacer con esto?

# frozen_string_literal: true
messanjah
fuente

Respuestas:

314

# frozen_string_literal: truees un comentario mágico, admitido por primera vez en Ruby 2.3, que le dice a Ruby que todos los literales de cadena en el archivo están implícitamente congelados, como si se #freezehubiera llamado a cada uno de ellos. Es decir, si un literal de cadena se define en un archivo con este comentario, y llama a un método en esa cadena que lo modifica, como <<, obtendrá RuntimeError: can't modify frozen String.

El comentario debe estar en la primera línea del archivo.

En Ruby 2.3, puede usar este comentario mágico para prepararse para los literales de cadena congelados que son los predeterminados en Ruby 3 .

En Ruby 2.3 se ejecuta con la --enable=frozen-string-literalbandera, y en Ruby 3, los literales de cadena se congelan en todos los archivos. Puede anular la configuración global con # frozen_string_literal: false.

Si desea que un literal de cadena sea mutable independientemente de la configuración global o por archivo, puede prefijarlo con el +operador unario (teniendo cuidado con la precedencia del operador) o invocarlo .dup:

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

También puede congelar una cadena mutable (sin congelar) con unario -.

Dave Schweisguth
fuente
24
Algo importante a tener en cuenta con respecto a la congelación de cadenas es que mejora el rendimiento de la aplicación . Ver también aquí
Andres Ehrenpreis
2
@ dave-schweisguth ¿No deberíamos esperar -"foo"ser lo mismo que "foo".freeze? Cuando verifico (-"foo").__id__, obtengo un valor diferente cada vez, pero "foo".freeze.__id__es el mismo cada vez. ¿Algunas ideas?
lilole
Me pregunto si esta función es el problema, parece que solo se llama con el unario menos. github.com/ruby/ruby/blob/trunk/string.c#L2572
lilole
2
-es para deduplicar la cadena para ahorrar memoria, además de devolver una cadena congelada.
eregon
99
Si bien aún puede usar el comentario mágico, Matz decidió oficialmente no hacer que todos los literales de cadena sean inmutables por defecto en Ruby 3: bugs.ruby-lang.org/issues/11473#note-53
Konstantin Tikhonov
44

Mejora el rendimiento de la aplicación al no asignar nuevo espacio para la misma cadena, lo que también ahorra tiempo para las tareas de recolección de basura. ¿Cómo? cuando congela un literal de cadena (objeto de cadena), le está diciendo a Ruby que no permita que ninguno de sus programas modifique el literal de cadena (objeto).

Algunas observaciones obvias a tener en cuenta.

1. Al congelar los literales de cadena, no está asignando nuevo espacio de memoria para ello.

Ejemplo:

Sin comentario mágico asigna nuevo espacio para la misma cadena (Observe las diferentes ID de objeto impresas)

def hello_id
  a = 'hello'
  a.object_id
end

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

Con un comentario mágico , ruby ​​asigna espacio solo una vez

# frozen_string_literal: true

def hello_id
  a = 'hello'
  a.object_id
end

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

2. Al congelar los literales de cadena, su programa generará una excepción al intentar modificar el literal de cadena.

Ejemplo:

Sin comentarios mágicos , puede modificar los literales de cadena.

name = 'Johny'
name << ' Cash'

puts name     #=> Johny Cash

Con un comentario mágico , se generará una excepción cuando modifique literales de cadena

# frozen_string_literal: true

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

puts name      

Siempre hay más para aprender y ser flexible:

imechemi
fuente
Esta es una respuesta más intuitiva.
Jin Lim
20

En Ruby 3.0. Matz (el creador de Ruby) decidió hacer todos los literales de cadena congelados por defecto.

Puedes usar en Ruby 2.x. Simplemente agregue este comentario en la primera línea de sus archivos.

# frozen_string_literal: true

El comentario anterior en la parte superior de un archivo cambia la semántica de los literales de cadena estática en el archivo. Los literales de cadena estática se congelarán y siempre devolverán el mismo objeto. (La semántica de los literales de cadena dinámica no cambia).

De esta manera tiene los siguientes beneficios:

Sin feo sufijo f. No hay error de sintaxis en Ruby anterior. Solo necesitamos una línea para cada archivo.

Por favor, lea este tema para obtener más información.

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

Alexandr
fuente
Desafortunadamente, este comentario no funciona para las cadenas en matrices, por lo que aún deben
congelarse
3
Lamentablemente, esto no estará en ruby ​​3 bugs.ruby-lang.org/issues/11473#note-53
zhisme