はじめに
現在、プログラミングスクールにて、主にRubyやRuby on Railsを勉強している者です。
Railsのコードの中で、ぼっち演算子を見かけた際に、調べれば調べるほどRubyへの理解が乏しいと感じたため、記事にさせていただきました。
ぼっち演算子とは
ぼっち演算子(セーフナビゲーション演算子とも呼ばれる)とは、「&.」という記号で表されるRubyの演算子となります。この演算子を使用することで、オブジェクトがnilの場合にメソッド呼び出しを安全にスキップすることができます。
ちなみに、ぼっち演算子はRubyの2.3から実装された機能です。
安全にスキップできるとは?
Rubyでは、オブジェクトに紐づいたメソッドを呼び出そうとしたとき、そのオブジェクトがnilだった場合にNoMethodErrorという名前のエラーが発生します。
ぼっち演算子を使用することで、そのオブジェクトがnilだった場合に、紐づいたメソッドを実行することなく、nilを返すことができ、NoMethodError になることを防いでくれます。
NoMethodErrorの例(ぼっち演算子を用いなかった場合)
irb(main):009:0> text = nil
=> nil
irb(main):010:0> text.upcase
(irb):10:in `<main>': undefined method `upcase' for nil:NilClass (NoMethodError)
ぼっち演算子を用いた場合
irb(main):016:0> text = nil
=> nil
irb(main):017:0> text&.upcase
=> nil
text変数がnilのため、後続のupcaseメソッドの呼び出しはスキップされ、結果としてnilが返されることが確認できました。このように、nilを返すことにより、不要なエラーハンドリング(NoMethodErrorへの対応処理)や条件分岐を書く手間を省き、コードをシンプルに読みやすく書くことができます。
例:ぼっち演算子を使わない場合のnilの確認。
if text
result = text.upcase
else
result = nil
end
※nilにもメソッドもあり
Rubyのnilもオブジェクトであり、NilClassというクラスのインスタンスです。
そのため、NilClassに定義されているメソッドは、nilオブジェクトに対しても呼び出すことができます。
例:
irb(main):020:0> text = nil
=> nil
irb(main):021:0> text.nil?
=> true
上記の例では、nil?メソッドはNilClassに定義されているメソッドなので、text(nil)に対して呼び出すことができました。
しかし、先のupcaseメソッドの例のように、NilClassに定義されていないメソッドをnilオブジェクトに対して呼び出そうとすると、NoMethodErrorが発生します。そのため、常にNoMethodErrorが発生するとは限らないことには注意してください。
レシーバについて
text.upcaseのような形式でメソッドを呼び出すとき、.の前のtext部分を「レシーバ」と呼びます。このレシーバは、メソッド呼び出しの「対象」となるオブジェクトを指します。具体的には、upcaseメソッドをどのオブジェクトに対して適用するかを示す役割を持っています。この文脈で「オブジェクト」という言葉を使用していますが、実際にはこの「オブジェクト」が「レシーバ」として機能しているわけです。
また、Rubyではすべてがオブジェクトとして扱われます。このため、変数、リテラル、さらにはクラスやモジュールもメソッドのレシーバとして動作することができます。レシーバはオブジェクト自身を指すという表現を見ることがあるかと思いますが、今回の説明でしっくりくればと思い補足いたしました。
Railsの課題でよく見かけた(る)コード
最後に、Railsのコード中で使用されるぼっち演算子に焦点を当てていきます。
以下のコードは、MVCの基本的な動きを理解している方向けに簡略化されてることをご了承いただきたく存じます。
<% if current_user&.mine?(post) %>
<%= link_to '編集', edit_post_path(post), ~省略~ %>
<%= link_to '削除', post_path(post), ~省略~ %>
<% end %>
def mine?(object)
id == object.user_id
end
def current_user
@current_user ||= User.find_by(id: session[:user_id])
end
このRailsのコードは、ログインしているユーザーが特定の投稿(post)のオーナーである場合、その投稿に対する編集・削除リンクを表示するものです。
-
current_user&.mine?(post) で、current_user は現在ログインしているユーザーのオブジェクトを返すメソッドです。ログインしていない場合、nil を返します。
-
mine?(post) メソッドは、ログインユーザーが投稿のオーナーか確認するためのもの。このメソッド内の id は、self.id を指し、ここでの self は current_user が返す User インスタンスを指します。
-
ぼっち演算子 &. を使用することで、current_user が nil の場合も、メソッド呼び出しをスキップできます。
このコードによって、ログイン中のユーザーだけが自分の投稿を編集・削除できるようになります。そして、ぼっち演算子の使用により、ログインしていない場合や不正なアクセスがあった場合でも、エラーを回避することができます。
selfについて
Rubyにおいて、selfは現在のオブジェクト自体を参照する特殊な変数です。メソッド内で使用されると、そのメソッドが呼び出されたオブジェクトを指します。言い換えれば、selfはそのメソッドの「レシーバ」として動作します。
例えば、上記のmine?(object)メソッドにおいて、idは実際にはself.idという形で書くこともできます。selfはmine?メソッドを呼び出したオブジェクト、つまりcurrent_userを指しています。
def mine?(object)
self.id == object.user_id
end
しかし、Rubyの慣習として、インスタンス変数にアクセスする場合やインスタンスメソッドを呼び出す際に、明示的にselfを指定する必要はありません。そのため、idのようにselfを省略して書かれることが多いです。