コメント「frozen_string_literal:true」は何をしますか?


226

これはrspec私のプロジェクトディレクトリのbinstubです。

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

回答:


314

# frozen_string_literal: trueRuby 2.3で初めてサポートされたマジックコメントで、ファイル内のすべての文字列リテラルが、#freezeそれぞれに対して呼び出されたかのように暗黙的に凍結されることをRubyに伝えます。文字列リテラルは、このコメントのファイルに定義されている場合つまり、とあなたはどのような修正を、その文字列のメソッドを呼び出して<<、あなたが得るでしょう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

unaryを使用して、変更可能な(フリーズされていない)文字列をフリーズすることもでき-ます。


24
文字列のフリーズに関して注意すべき重要なことは、アプリのパフォーマンスが向上することです。こちらもご覧ください
Andres Ehrenpreis 2017年

2
@ dave-schweisguth -"foo"と同じであると期待すべきではありません"foo".freezeか?チェックする(-"foo").__id__と、毎回異なる値が得られますが、毎回"foo".freeze.__id__同じです。何か案は?
リロール

この機能が問題なのかと思いますが、単項マイナスでしか呼び出されないようです。github.com/ruby/ruby/blob/trunk/string.c#L2572
リロール

2
-凍結された文字列を返すことに加えて、メモリを節約するために文字列を重複排除するためのものです。
エレゴン2018

9
マジックコメントは引き続き使用できますが、MatzはRuby 3ですべての文字列リテラルをデフォルトで不変にしないことを正式に決定しました:bugs.ruby-lang.org/issues/11473#note-53
Konstantin Tikhonov

44

同じ文字列に新しいスペースを割り当てないことでアプリケーションのパフォーマンスが向上し、ガベージコレクションの雑用の時間も節約されます。どうやって?文字列リテラル(文字列オブジェクト)をフリーズすると、Rubyにプログラムに文字列リテラル(オブジェクト)を変更させないように指示することになります。

覚えておくべきいくつかの明白な観察。

1.文字列リテラルをフリーズすることにより、そのための新しいメモリ空間を割り当てません。

例:

マジックコメントがない場合、同じ文字列に新しいスペースが割り当てられます(出力された異なるオブジェクトIDを確認してください)

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      

学ぶことと柔軟であることは常にもっとあります:


これはより直感的な答えです。
ジンリム

20

Ruby 3.0。Matz(Rubyの作成者)は、すべての文字列リテラルをデフォルトで凍結することを決定しました。

Ruby 2.xで使用できます。このコメントをファイルの最初の行に追加するだけです。

# frozen_string_literal: true

上記のファイル上部のコメントは、ファイル内の静的文字列リテラルのセマンティクスを変更します。静的文字列リテラルは凍結され、常に同じオブジェクトを返します。(動的文字列リテラルのセマンティクスは変更されません。)

この方法には次の利点があります。

醜いfサフィックスはありません。古いRubyでは構文エラーはありません。各ファイルに必要なのは1行だけです。

詳細については、このトピックをお読みください。

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


残念ながら、このコメントは配列内の文字列には機能しないため、明示的にフリーズする必要があります
ToTenMilan

3
残念ながら、これはルビー3になりませんbugs.ruby-lang.org/issues/11473#note-53
zhisme
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.