Rubyでの文字列連結に「#+」ではなく式展開「#{}」を使うべき理由

こんにちは、hachi8833です。 Slackでmorimorihogeさんが投げてくれたアドバイスのメモを残します。上のスクショはGitLabのmerge requestにmorimorihogeさんがコメントしたものです。 Rubyでの文字列連結メソッド`#+`と`#{}`は同じではない! Rubyの文字列連結メソッド#+と#{}は、通常次のように使われます。 str2 = “おいしい” str1 = str2 + “店” #=> 「#+」は変数同士や変数と文字列リテラルを連結する str1 = “#{str2}店” #=> 「#{}」は” “の中で変数内の文字列を取り出す(式展開) #上のどちらも出力は「おいしい店」になる 後者の式展開(interpolation)は変数展開などとも呼ばれ、RubyやPythonなどのモダンな言語で文字列内に変数の値を置くときに使われる定番の手法です(追伸: JavaScriptではtemplate literalと呼ばれています)。 Rubyの文字列連結は上の例のどちらの方法を使っても同じになりそうな気がしますが、実は同じになるとは限りません。 Rubyでは次に挙げる理由から、ぜひとも後者の式展開「#{}」を使うことをおすすめします。 理由1: 式展開なら自動で#to_sが効く 式展開#{}の変数は、出力時に自動的に#to_sが行われます。#to_sは文字列でない値を文字列に変換するメソッドであり、しかも値がnilの場合には””を出力してくれるので、無駄なエラーが発生しません。 理由2: 式展開の#to_sはどんなオブジェクトにも使える #to_sメソッドはObjectクラスにあります。言うまでもなくRubyではすべてのものがオブジェクトなので、#to_sメソッドはほぼどんなオブジェクトに対してもエラーなしで確実に実行できます。 「ほぼ」と書いたのは、Ruby 1.9以降の継承リストのルートにあるのはObjectクラスではなくBasicObjectだからです(クラスの継承リスト: Object < Kernel < BasicObject)。BasicObjectクラスにはほとんどメソッドがなく、#to_sといえどもさすがにエラーになります(使う人はいないと思いますが)。 詳しくはObjectクラスとBasicObjectをご覧ください。 理由3:`#+`で連結すると順序によって結果が異なることがある 文字列連結であればどんな順序であろうと挙動が変わらないように思えますが、変数と文字列を#+で連結すると、変数の内容によって以下のような問題が生じる可能性があります。 変数がもしFixnumだったら: [1] pry(main)> 1 + ‘hoge’ TypeError: String can’t be coerced into Fixnum from (pry):1:in `+’ [2] pry(main)> ‘hoge’ + 1 TypeError: no implicit conversion of Fixnum into String from (pry):2:in `+’ 変数がもしnilだったら: [3] pry(main)> ‘hoge’ + nil TypeError: no implicit conversion of nil into String from (pry):3:in `+’ [4] pry(main)> nil + ‘hoge’ NoMethodError: undefined method `+’ for nil:NilClass from (pry):4:in `<main>’ Rubyの#+で文字列と変数を結合すると、当初は正常に動作したとしても後で思わぬトラップを踏むかもしれません。 #+による文字列連結を使うなら、少なくとも変数を先行させないようにし、変数に文字列以外の値が使われないよう注意する必要があります。 しかし式展開#{}ならそうした気遣いはまったく不要になります。実際、前述の#+の問題はすべて式展開#{}で正常に動作します。 変数含みの文字出力には常に式展開#{}を使うことをおすすめします。 元レビューの補足(2016/09/06 13時ごろ追記) 元のMRコメントを書いたmorimorihogeです。 当該コメントは全体としては以下な感じでもうちょっと詳しく書いてました。 ↑スクショにもある通り、このコメントをした事例においては変数オブジェクトがStringであることが保証されていたので「直す必要はない」としたわけですね。 そこそこRubyを書いていると式展開なんかは当たり前のことではあるのですが、新人さんやRuby自体にまだ慣れていないメンバもいる中ではあえて常識と思っていることもしっかり解説するのが大事かな、と思って普段レビューしています。 元レビューも単に「式展開を使って下さい」でも良かったのですが、なぜ式展開を使うのか、どうして式展開がbetterなのかを知っておくことで今後思わぬ地雷を踏まなくなるといいなと思った次第です。Railsはビューの中でエラーが出るとデフォルトでは問答無用でアプリケーションエラーページが出てしまいますので、#tryや#digなど、多少想定外のデータが来ても良い書き方をするクセを付けておくのが良いかと思います。 関連記事 Rubyの式展開(string interpolation)についてまとめ: `#{}`、`%`、Railsの`?` RailsビューのHTMLエスケープは#link_toなどのヘルパーメソッドで解除されることがある ActiveRecordで日付・時刻の範囲検索をシンプルに書く方法