Co robi komentarz „frozen_string_literal: true”?

226

To jest rspecbinstub w moim katalogu projektu.

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

Co to ma zrobić?

# frozen_string_literal: true
mesanjah
źródło

Odpowiedzi:

314

# frozen_string_literal: trueto magiczny komentarz, obsługiwany po raz pierwszy w Ruby 2.3, który mówi Ruby'emu, że wszystkie literały łańcuchowe w pliku są domyślnie zawieszone, jakby #freezezostały wywołane na każdym z nich. To znaczy, jeśli dosłowny ciąg znaków jest zdefiniowany w pliku z tym komentarzem i wywołujesz metodę na tym ciągu, która go modyfikuje, na przykład <<otrzymasz RuntimeError: can't modify frozen String.

Komentarz musi znajdować się w pierwszym wierszu pliku.

W Ruby 2.3 możesz użyć tego magicznego komentarza do przygotowania zamrożonych literałów łańcuchowych, które są domyślne w Ruby 3 .

W Ruby 2.3 uruchamianym z --enable=frozen-string-literalflagą, aw Ruby 3 literały łańcuchowe są zamrażane we wszystkich plikach. Możesz zastąpić ustawienie globalne za pomocą # frozen_string_literal: false.

Jeśli chcesz, aby literał łańcuchowy był modyfikowalny niezależnie od ustawienia globalnego lub dla poszczególnych plików, możesz poprzedzić go jednoargumentowym +operatorem (uważając na pierwszeństwo operatora) lub wywołać .dupgo:

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

Możesz także zamrozić zmienny (niezamrożony) ciąg z unarnym -.

Dave Schweisguth
źródło
24
Ważną rzeczą, na którą należy zwrócić uwagę w przypadku blokowania łańcuchów, jest to, że poprawia to wydajność aplikacji . Zobacz także tutaj
Andres Ehrenpreis
2
@ dave-schweisguth Czy nie powinniśmy oczekiwać, że będziemy tacy -"foo"sami jak "foo".freeze? Kiedy sprawdzam (-"foo").__id__, za każdym razem otrzymuję inną wartość, ale za "foo".freeze.__id__każdym razem jest taka sama. Jakieś pomysły?
lilole,
Zastanawiam się, czy ta funkcja jest problemem, wydaje się, że jest wywoływana tylko z jednoznacznym minusem. github.com/ruby/ruby/blob/trunk/string.c#L2572
lilole
2
-służy do deduplikacji ciągu w celu zaoszczędzenia pamięci, oprócz zwracania zamrożonego ciągu.
eregon
9
Chociaż nadal możesz używać magicznego komentarza, Matz oficjalnie postanowił, że wszystkie literały łańcuchowe nie będą domyślnie niezmienne w Ruby 3: bugs.ruby-lang.org/issues/11473#note-53
Konstantin Tikhonov
43

Poprawia wydajność aplikacji, nie przydzielając nowego miejsca dla tego samego łańcucha, a tym samym oszczędzając czas na prace związane z odśmiecaniem. W jaki sposób? kiedy zamrażasz literał łańcuchowy (obiekt łańcuchowy), mówisz Ruby'owi, aby żaden program nie modyfikował literału łańcuchowego (obiekt).

Kilka oczywistych spostrzeżeń, o których należy pamiętać.

1. Zamrażając literały łańcuchowe, nie przydzielasz dla niej nowej przestrzeni pamięci.

Przykład:

Bez magicznego komentarza przydziela nowe miejsce dla tego samego łańcucha (Obserwuj różne wydrukowane identyfikatory obiektów)

def hello_id
  a = 'hello'
  a.object_id
end

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

Z magicznym komentarzem ruby przydziela przestrzeń tylko raz

# frozen_string_literal: true

def hello_id
  a = 'hello'
  a.object_id
end

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

2. Zamrażając literały łańcuchowe, twój program zgłosi wyjątek podczas próby modyfikacji literałów łańcuchowych.

Przykład:

Bez magicznego komentarza możesz modyfikować literały łańcuchowe.

name = 'Johny'
name << ' Cash'

puts name     #=> Johny Cash

Z magicznym komentarzem powstanie wyjątek podczas modyfikowania literałów łańcuchowych

# frozen_string_literal: true

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

puts name      

Zawsze musisz nauczyć się więcej i być elastycznym:

imechemi
źródło
To jest bardziej intuicyjna odpowiedź.
Jin Lim
20

W Ruby 3.0. Matz (twórca Ruby) postanowił domyślnie zamrozić wszystkie literały String.

Możesz używać w Ruby 2.x. Po prostu dodaj ten komentarz w pierwszym wierszu swoich plików.

# frozen_string_literal: true

Powyższy komentarz w górnej części pliku zmienia semantykę literałów ciągów statycznych w pliku. Literały ciągów statycznych zostaną zamrożone i zawsze zwracają ten sam obiekt. (Semantyka dynamicznych literałów łańcuchowych nie ulega zmianie).

Ten sposób ma następujące zalety:

Bez brzydkiego sufiksu F. Brak błędu składniowego na starszym Ruby. Potrzebujemy tylko linii dla każdego pliku.

Proszę, przeczytaj ten temat, aby uzyskać więcej informacji.

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

Alexandr
źródło
Niestety ten komentarz nie działa na łańcuchy w tablicach, więc nadal trzeba je wyraźnie
zamrozić
3
Niestety nie będzie to w ruby ​​3 bugs.ruby-lang.org/issues/11473#note-53
zhisme