質問: 前置き ---- 突然の投稿失礼します。 Ruby開発初心者なのですが、 知識と経験不足で開発の定石や基礎部分がうまく理解できておらず質問を投稿させていただきます。 先月Rubyで開発を行い、ソースコードのレビューで指摘を頂いた時に色々と疑問に思ったことがあり、 その中で一点どうしても理解できないところがありましたのでご助言をお願いします。 指摘を受けた部分は、rubocopで構文チェックを行いソースコードを訂正した箇所にあります。 rubocopを実行させた際に ``` Missing frozen string literal comment. ``` という上記の警告が発生したため、調査して下記のコメントを付与することで問題を解決していました。 ``` # frozen_string_literal: true ``` この対応を行った箇所に対して 「その記述は必要ですか?」 というご指摘をレビューの際にいただき、今一度必要性を調査したところ謎が深まりわからなくなりました。 どうかご教示をお願いします。 ### 環境 Ruby : 2.3.1 Rails : 5.0.2 構文チェック : rubocop ### 疑問点 * 通常の開発を行う際は、rubocopのfrozen_string_literalに関してどう対応しているのか。 (.rubocop.ymlに設定するのか、警告の出たソースコードを修正する等) ※その対応の理由も教えていただきたいです。 調査したところ、文字列リテラルの扱いに関する記述・対応ということ、 rubocopが標準でfrozen_string_literalを検査していること、 またrubyのversionが3.0になる際に大きく関わるということは多少理解しました。 ↓参考にさせて頂いた資料 http://d.hatena.ne.jp/ku-ma-me/20151004/p1 http://www.task-notes.com/entry/20160831/1472572735 見当外れなことをお聞きしているかもしれませんが、是非ご教示お願いします。 よろしくお願いします。 必要な情報などがございましたらご提示致しますのでその際はお手数ですがお声掛けしていただけると幸いです。
回答: ### frozen_string_literalについて rubyにおいて、文字列(Stringオブジェクト)は**mutable(破壊的に変更可能)**なデータです。 ruby reference: class String 例えば、以下のコードは変数str/str2に一度しか代入していないにもかかわらず、参照のタイミングによって内容が異なっています。 ``` str = 'test' str2 = str print str, str2 # => testtest str.upcase! print str, str2 # => TESTTEST ``` 一般に、オブジェクトの破壊的変更はコードの可読性を下げ、バグを誘発する傾向にあります。あるオブジェクトをコードのどこかで破壊的に変更すると、そのオブジェクトを参照している箇所全体に影響を与えるからです(上記コードのstr/str2の関係)。 そこで、従来のrubyでは文字列の破壊的変更を禁止するため、`freeze`メソッドを呼ぶといった対策を取ってきました。 ``` str = 'test' str.freeze str.upcase! # => RuntimeError: can't modify frozen String ``` しかし、文字列というごく一般的なデータを取り扱うために都度freezeを付けるのは面倒臭い + freezeが必要/不要を考えず、文字列に片っ端からfreezeを付ける実装が跋扈してきたということから、**「そもそもデフォルトで文字列をimmutable(破壊的な変更不可)にしてしまおう」**という方針が、ruby3.0以降で**決定**しています。 [Ruby] Ruby 3.0 の特大の非互換について そして、将来ruby3.0がリリースされた時にruby2.x系からスムーズに以降できるよう、「デフォルトでStringをimmutableにする」という機能をruby2.x系で限定的に取り込んだのがfrozen_string_literalというマジックコメントです。 ### frozen_string_literalの対応 私は原則、rubocopの指針に従ってfronze_string_literal: trueをソースコード内に追加するようにしています。その方が、将来バージョンアップ時の手間が減る(ruby3.0では、frozen_string_literalは単なるコメントとして扱われる予定)ことと、デフォルトでimmutableにした方が予期せぬバグを抱えにくいからです。 ただ、config系のファイル、テストコード、migrationファイルはrubocopによるチェックの対象外としています。 migrationファイルはほぼrailsで自動生成したものをそのまま使う、configはそもそも設定を書き下すだけでデータ操作はしない、テストコードは実際の動作に関わらない、といった理由から、そこまで厳密なコーディングをしなくても良いだろう、という判断です。