Code Fix

中級

初期化前のインスタンス変数を参照すると警告なしで nil になる

initializeで値を設定し忘れたインスタンス変数は、エラーにも警告にもならず単にnilとして扱われます。存在しないローカル変数がNameErrorになるのとは対照的です。

なぜエラーが出ないのか

出力: Balance: 
(エラーなし)
Rubyは何も報告しません。文法として正しいためです
出力: Balance:
実際の挙動 — 期待した結果と食い違っている箇所
見つけ方
エラーが出ないので、出力を目で確かめるしかありません。この種の誤りが最も発見が遅れます

このエラーが出る典型パターン

パターン1

 1  class Account
 2    def initialize(balance)
 3      balance  = balance
            ^
 4    end
 5  
 6    def show
 7      puts "Balance: #{@balance}"
 8    end
 9  end
10  
11  Account.new(500).show
出力: Balance:

@を付けずに代入するとローカル変数になり、initializeを抜けた時点で消えます。@balanceは初期化されないままnilで残ります。

直し方: balance を @balance にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  class Player
 2    def initialize(score)
 3      score  = score
           ^
 4    end
 5  
 6    def show
 7      puts "Score: #{@score}"
 8    end
 9  end
10  
11  Player.new(80).show
出力: Score:

エラーにも警告にもならないため、出力を確認するまで初期化し忘れに気づけません。

直し方: score を @score にします。

この問題を解いてみる →

パターン3

 1  class Character
 2    def initialize(level)
 3      level  = level
           ^
 4    end
 5  
 6    def show
 7      puts "Level: #{@level}"
 8    end
 9  end
10  
11  Character.new(5).show
出力: Level:

インスタンス変数のつもりで書いたローカル変数は、メソッドを抜けると同時に消えてしまいます。

直し方: level を @level にします。

この問題を解いてみる →

パターン4

 1  class Cart
 2    def initialize(count)
 3      count  = count
           ^
 4    end
 5  
 6    def show
 7      puts "Count: #{@count}"
 8    end
 9  end
10  
11  Cart.new(3).show
出力: Count:

未初期化のインスタンス変数はNameErrorにならず、静かにnilとして扱われます。

直し方: count を @count にします。

この問題を解いてみる →

パターン5

 1  class Order
 2    def initialize(total)
 3      total  = total
           ^
 4    end
 5  
 6    def show
 7      puts "Total: #{@total}"
 8    end
 9  end
10  
11  Order.new(999).show
出力: Total:

@の付け忘れは見た目がよく似ているため、コードを読むだけでは見落としやすいミスです。

直し方: total を @total にします。

この問題を解いてみる →

よくある誤解

「初期化していないインスタンス変数を参照すればエラーになるはず」という思い込みは誤りです。Rubyのインスタンス変数は未初期化でもnilという値を持つ変数として静かに扱われ、後続の計算にnilが紛れ込みます。

実務での勘所

ローカル変数と違いインスタンス変数が警告なしでnilになるのは、Rubyがそのオブジェクトにどんなインスタンス変数が存在しうるかを事前に把握できないためです。ローカル変数はそのメソッドの中のコードを読めば、代入されている変数の一覧をあらかじめ確定できます。インスタンス変数はinitializeの外からでも実行時に動的にいつでも代入できてしまうため、「このオブジェクトが最終的に持つインスタンス変数の一覧」をコードを読んだだけで確定する方法がありません。確定できない以上、Rubyは未初期化かどうかを判定する手立てを持たず、単にnilとして扱うしかないのです。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)