【Rails】undefined method `メソッド名’ for nil:NilClass (NoMethodError) の原因と解決手順【初心者向け】

Ruby on Rails

undefined method 〜 nil:NilClass というエラーメッセージが解決できません・・・

そんな疑問にお答えします!

Ruby や Ruby on Rails を学習していると一番多く発生するエラー

undefined method `メソッド名' for nil:NilClass (NoMethodError)

です。

この記事にたどり着いた方はこのエラーに苦しめられているのではないでしょうか。

本記事はこのエラーを自力で解決できる力を得られる内容になっています。

多くのプログラミング学習者を支援してきましたが、本記事で解説する原因と解決手順をお伝えするとみなさん自分で解決できるようになりました。

本記事では、このエラーに苦しめられなくなるように、原因解決手順について解説します。

この記事を読めばエラーを手早く解決できる力がつきま

スポンサーリンク

エラーの原因: そのメソッドを呼び出そうとしている値(レシーバ)が nil

nil に対してメソッド名を呼び出したけど定義されていないためエラーです。

そのメソッドを呼び出そうとしている値が nil になっています。

呼び出そうとしている値というのは、ドットの前です。

ドットの前をレシーバと呼びます。

例えば、

foo.bar

の場合は、変数 foo に入っている値がレシーバです。

エラーが

undefined method `bar' for nil:NilClass (NoMethodError)

で、エラー発生箇所が

foo.bar

であれば

  • foo が変数 → foo に入っている値が nil
  • foo がメソッド呼び出し → foo の戻り値が nil

ということになります。

「変数のパターン」と「メソッド呼び出しのパターン」、それぞれ見ていきましょう。

変数のパターン

以下が foo が変数のパターンです。

# どこかで変数 foo に nil を代入している
foo = nil
・
・
・
# ここで undefined method `bar' 
# for nil:NilClass (NoMethodError)
# が発生
foo.bar

メソッドのパターン

以下が foo がメソッドのパターンです。

# メソッド foo は nil を返す
def foo
  ・
  ・
  ・
  return nil
end

# ここで undefined method `bar' 
# for nil:NilClass (NoMethodError)
# が発生
foo.bar

引数のパターン

変数の亜種として、foo がメソッドの引数というパターンもあります。

def baz(foo)
  # ここで undefined method `bar' 
  # for nil:NilClass (NoMethodError)
  # が発生
  foo.bar
end

# 引数に nil を与えてメソッド呼び出し
baz(nil)

他にも細かいパターンはありますが、いずれもレシーバがどのような値なのかを把握することが大切です。

解決できない場合: Ruby、Ruby on Rails の基礎に理解不足が無いか要確認

エラーが解決できない場合、実はエラー自体ではなく前提知識に理解不足があることが多いです。

ここまで読んで何が起きているのか分からないという方は以下のような前提知識に理解不足は無いか確認してみましょう。

エラーが発生するとどうしてもエラーの解決自体に集中してしまいがちですが、前提知識に不足があるとエラー解決自体ができず時間を無駄にしてしまいます

これまで100名以上のプログラミング学習の支援をしてきた経験から、
「これが理解できていないせいで解決できない」という前提知識を列挙しておきます。

よくある不足知識一覧

Ruby

  • 変数
  • 変数の定義の仕方
  • インスタンス
  • インスタンスメソッド
  • インスタンスメソッドに対するメソッドの呼び出し方
  • メソッドの引数
  • メソッドの戻り地
  • ブロック構文とブロック引数(特に each メソッドによるループ)

Ruby on Rails

  • ルーター(routes.rb) の定義によるコントローラ・アクションの呼び出しルール
  • コントローラからビューを呼び出すルールと、インスタンス変数による値の共有
  • ビューからビューを呼び出す(パーシャル)ときに変数を指定する方法

一つでも答えられないものがあった場合、そこを調べて理解してみましょう。

前提知識を理解してから再度確認するとアッサリと分かったりします。
何事も基礎が大切ですね。

解決手順

それでは

undefined method `メソッド名' for nil:NilClass (NoMethodError)

の解決手順を解説します。

手順の概要は以下の通りです。

  • 手順①: エラー発生箇所の確認
  • 手順②: エラーから集めた情報を元に何が nil なのかを推理する
  • 手順③: プログラムを逆順にたどって、レシーバがなぜ nil なのかを確認する
  • 手順④: 仕様と、確認した理由をもとに対処法を考える

順に確認していくことが大切です。

手順①: エラー発生箇所の確認

エラー画面から情報を読み取っていきます。

一つずつ見ていきましょう。

エラーの概要

最初の行でエラーの種類と発生した箇所(コントローラ+アクション)が分かります。

この例の場合は、

  • NoMethodError というエラーが発生している
  • 発生した箇所は
    • コントローラ名: PostsController
    • アクション名: show

ということが分かります。

エラー発生箇所

次の行はエラー発生箇所を示しています。

この例では

app/views/posts/show.html.erb の 6 行目

で発生しているということが分かります。

エラーメッセージ

次の行はエラーメッセージを表示しています。

このエラーメッセージが一番むずかしいですが一番大切なところです。

初学者の場合ここが難しいので読み飛ばしてしまいがちですが、ここをしっかり読まないとエラー解決はほぼ無理なので苦手意識を持たずに挑戦しましょう。

このメッセージは

NilClass の nil に対して content というメソッドは定義されていない

と言っています。

裏を返せば

nil に対して content を呼び出そうとした

ということです。

エラー発生箇所のコード

次の行ではエラー発生箇所のコードを示しています。

赤背景の行が発生箇所です。つまり

<td><%= @post.content %></td>

でエラーが起きていることが分かります。

手順②: エラーから集めた情報を元に何が nil なのかを推理する

手順①で集めた情報を元に何が nil なのかを推理します。

エラーメッセージから

nil に対して content を呼び出そうとした

というのが結論でした。

一方エラー発生箇所のコードでは

<td><%= @post.content %></td>

となっています。

メソッド呼び出しは、

レシーバ.メソッド

です。

該当行のうち、content メソッドの呼び出し箇所は

@post.content

となります。

つまり content メソッドを呼び出そうとしているのはインスタンス変数 @post の中身となります。

整理すると、

  • nil に対して content を呼び出した (←エラーメッセージより)
  • インスタンス変数 @post の中身に対して content メソッドを呼び出そうとしている(←ソースコードより)

となります。

ここから導かれる結論は

インスタンス変数 @post の中身は nil である

ということです。

手順③: プログラムを逆順にたどって、レシーバがなぜ nil なのかを確認する

手順①で「インスタンス変数 @post が nil である」ということが分かりました。

次にすることは、「それではなぜ @post が nil なのか」を把握することです。

これを把握するためには、プログラムコードを実行の逆順にたどっていく必要があります。

ファイル内で定義されているかを確認

まず、エラーの箇所をもう一度確認します。

<table>
  <tr>
    <td>本文</td>
    <td><%= @post.content %></td>
  </tr>
</table>

「インスタンス変数 @post が nil」という結論だったわけですが、@post がどこで定義されているかを上をたどっていきます。

少なくともこのファイルでは @post は代入文が無いので、もし定義されているとしたら呼び出し元であることがわかります。

呼び出し元をたどる

呼び出し元をたどっていきます。

このビューファイルは以下の Posts#show アクションから呼び出されています。

class PostsController < ApplicationController
  def show
  end
end

show アクションのコードでも @post の代入文はありません。

また before_action も無いため、@post はここでも定義されていません。

さらにたどる

定義されていない場合はさらにたどっていくことになります。

ただ、アクションは該当するリクエストの起点なので、今回の例の場合はもうたどるコードがありません。

ということで 「@post はそのリクエストでは定義している箇所が無い」、ということが分かりました。

手順④: 仕様と、確認した理由をもとに対処法を考える

理由が分かったら対処方針を考えます。

「@post はそのリクエストでは定義している箇所が無い」ということが分かったので、対処方針としては以下が考えられます。

  • @post の定義を忘れている→ @post を定義する
  • 使うのは @post ではなかった → エラーの箇所の変数名を定義済みの変数名に変える

今回は、そもそも定義するのを忘れていたようなので、Posts#show アクションで定義してあげればよさそうです。

class PostsController < ApplicationController
  def show
    @post = Post.find(params[:id]) # 追加
  end
end

動作確認すると無事エラーを回避できました!

エラーの理由から考えられる対処方針は常に複数あることに注意

原因が分かったとしても対処法は常に複数あることに注意しましょう。

今回はインスタンス変数 @post が定義されていない→定義すればいい、ということでした。

しかし、別の可能性として、「@post ではなくて、ローカル変数の post だった」という可能性もあります。

例えば

<%= @posts.each do |post| %>
  <td><%= @post.content %></td> ← ここでエラー
<% end %>

上記の様な状況で2行目で

undefined method `content' for nil:NilClass (NoMethodError)

というエラーが出ているとしたら、実は一行目のブロック引数 post が正しかったということもありえます。

<%= @posts.each do |post| %>   ← ブロック引数 post が正しい場合
  <td><%= post.content %></td> ← @post を post に変更
<% end %>

@post が定義されていない→定義する、というのははじめに目が行きやすい対応方針ですが、常に別の可能性もあります。

常に仕様に照らし合わせて正しい対処法を考えてから修正することが大切です。
いきあたりばったりで修正するといいことは何もありません。

まとめ

エラー解決は

  1. エラーメッセージを理解し
  2. エラーの原因を確認し
  3. 確認できた状況から対応方法を考える

という手順で解決する必要があります。

解決できない場合は、特に初学者の場合はそのエラー自体が理解できないのではなく、前提知識に不足があることが多いです。

逆に言うとエラー解決の過程は前提知識の理解を深めるのにうってつけの機会ですので怖がらずチャレンジしていきましょう。

エラーに悩まされている初心者の方へ

大変な手順に感じましたか?
初心者のころはなかなかエラーが解決できずにイライラしますよね・・・。

実は現役エンジニアはこの手順を一瞬で終わらせることができるんです。
なぜなら現役エンジニアは何度も何度もエラーを解決してきたのでコツを掴んでいるから。
そんな現役エンジニアからエラー解決のコツを学んでみませんか?

「エンジニアに相談したい・・・でもそんなにお金はかけられない・・・」
そんな方におすすめのプログラミングスクールの紹介です。

  • 月額2,980円〜の低価格なサブスク
  • いつでも解約できる
  • 現役エンジニアへの質問が無制限

興味がある方は以下の記事からどうぞ。

その他の Rails エラー関連記事はこちら

タイトルとURLをコピーしました