現時点では解説がパーフェクトではないのを自覚しているが、めんどくさいので後でパーフェクトにする
Frozen String Literalとは
"str".freeze
のようなリテラルのこと。
Feature#8992 (r43627) でこのようなリテラルを書くと事前にallocate & freezeされた文字列オブジェクト(内部的にはfstringと呼ばれる) が取得されるようになり、Ruby 2.1以降ではイミュータブルな文字列が必要な場面でこのリテラルを使うと高速になる。
以下、それに関連した機能や最適化がどのバージョンで入ったか、各バージョンでのお勧めの書き方をまとめておく。
Ruby 2.0.0まで
まだ入ってないので気にしなくてよい
Ruby 2.1〜2.2
- Ruby 2.1: Use String#freeze and compiler tricks to replace "str"f suffix Feature#8992 (r43627)
-
"str".freeze
でfstringが取得されるようになった。
-
frozen? #=> false 向けの書き方
普通に""
と書くしかない。
frozen? #=> true 向けの書き方
"".freeze
と書くと速くなる。これ以外の書き方はない。
Ruby 2.3〜2.4
- Ruby 2.3: Immutable String literal in Ruby 3 Feature#11473 (r51659)
-
# frozen_string_literal: true
というコメントを最初に書くと文字列リテラルが全てfrozenな文字列を返すようになった。
-
- Ruby 2.3: String#+@ and String#-@ Feature#11782 (r52917)
-
String#+@
とString#-@
というメソッドが追加された。それぞれ単項演算子であり、+""
や-""
のように使える。
-
参考:String#+@
とString#-@
について
String#+@
- self が freeze されている文字列の場合、元の文字列の複製を返します。 freeze されていない場合は self を返します。
String#-@
- self が freeze されている文字列の場合、self を返します。 freeze されていない場合は元の文字列の freeze された複製を返します。
引用元:Ruby 2.4.0 リファレンスマニュアル > ライブラリ一覧 > 組み込みライブラリ > Stringクラス
frozen? #=> false 向けの書き方
-
# frozen_string_literal: true
を指定しているか、true/falseの両方に対応したい場合-
+""
が良い-
+""
はstr_uplus
がStringに直接定義されているのに対し、String#dup
はStringに対して直接定義されているわけではなく、rb_str_dup
等が直接呼び出されるようにはなっていないため、+""
の方が速い- 特に両方対応したい場合、
+""
はfrozen_string_literal: false
の時にrb_str_dup
の処理が走らないので高速
- 特に両方対応したい場合、
-
String.new
はリテラルではないのでスクリプトエンコーディングが効かず、エンコーディングがASCII-8bitになってしまうという問題がある。また、.new
のメソッド呼び出し以外にString
という定数の取得が走るため、定数取得の際インラインキャッシュは使われるものの微妙に遅い
-
-
-
# frozen_string_literal: false
を指定しているか、pragma自体を書いてない場合- 普通に
""
と書けばよい
- 普通に
frozen? #=> true 向けの書き方
-
# frozen_string_literal: true
を指定している場合-
""
と書けば良い
-
-
# frozen_string_literal: false
を指定しているか、pragma自体を書いてないか、true/falseの両方に対応したい場合-
"".freeze
と書くのが良い-
-""
とも書けるが、これはメソッド呼び出しが発生する上毎度別のオブジェクトが作られるため"".freeze
より遅くなってしまう
-
-
Ruby 2.5以降
- Ruby 2.5: apply opt_str_freeze to String#-@ ruby-core:80368 (r58144)
-
"".freeze
と同様、-""
でもメソッド呼び出しを経由しなくなり、また常に同じオブジェクトを返すようになったため高速になった
-
frozen? #=> false 向けの書き方
-
# frozen_string_literal: true
を指定しているか、true/falseの両方に対応したい場合-
+""
が良い- 理由はRuby 2.3〜2.4の時と同様
- 2023/11/20 追記: Ruby 3.3 では
String#dup
が高速化された https://github.com/ruby/ruby/pull/8952 ため、frozen_string_literal: true
時の"".dup
も+""
と同等の速度になりました
-
-
# frozen_string_literal: false
を指定しているか、pragma自体を書いてない場合- 普通に
""
と書けばよい
- 普通に
frozen? #=> true 向けの書き方
-
# frozen_string_literal: true
を指定している場合-
""
と書けば良い
-
-
# frozen_string_literal: false
を指定しているか、pragma自体を書いてないか、true/falseの両方に対応したい場合-
-""
か"".freeze
のどちらかが良い- どちらも速度は変わらないはずだが、短い上に、true/falseの両方に対応したい場合に
+""
と対称性のある-""
が個人的にはお勧め
- どちらも速度は変わらないはずだが、短い上に、true/falseの両方に対応したい場合に
-