ウェブエンジニア珍道中

日々の技術的に関する経験を書いていきます、脱線もします。

Rubyのセッターメソッドとレシーバとselfの話

Rubyでクラスを作って色々処理を書いていると、セッターメソッド周りで意図しない挙動をしたりRubocop(静的コード解析ツール)に「冗長なselfけんじゃねーよ」と怒られたりしたのでまとめます。

問題

問題のコードはこちらです、ギターをクラス化してみました。僕の趣味です。

class Guitar
  attr_accessor :color

  def initialize
    color = ''
  end

  def say_color
    "このギターは #{color} やで!"
  end
end

では色を表示してみましょう。

guitar = Guitar.new
puts guitar.color
puts guitar.say_color

実行結果

このギターは  やで!

うまく表示されません、つらい。

原因

原因はcolor = 'red'colorがローカル変数扱いされているからです。
Guitar#color=が呼び出されているのではなく、新しく変数を作っています。で、使わずにソッコー捨ててます。
で、インスタンス変数の@colornilのままです。

解決策

selfを付ければこのインスタンスのメソッドであることを明示できます。当てはめてみるとこんなコードになりますね。

class Guitar
  attr_accessor :color

  def initialize
    self.color = ''
  end

  def say_color
    "このギターは #{self.color} やで!"
  end
end

では再度色を表示してみましょう。

guitar = Guitar.new
puts guitar.color
puts guitar.say_color

実行結果

赤
このギターは 赤 やで!

ちゃんと表示されましたね。うまくインスタンス変数に値が入っています。

冗長だと怒られる問題

ではRubocopに通してみたいと思います。

self.rb:9:15: C: Redundant self detected.
    "このギターは #{self.color} やで!"

selfいらねーよ!」(意訳)と怒られてしまいました。ここはなくても大丈夫みたいですね。どこにいるのかいらないのかわからなくなってきました。

selfがいるところいらないところ

Rubyは変数の代入とセッターメソッドの違いをselfを使うことで区別するようです。
要はセッターメソッドの時にのみselfを付ければ良いわけですね。

def initialize
  #ここはRubyにとってはローカル変数かインスタンス変数かわからないのでローカル変数として扱う
  color = "" 
end

というわけでうまく動いて無駄なselfもないコードがこちらになります。

class Guitar
  attr_accessor :color

  def initialize
    self.color = ''
  end

  def say_color
    "このギターは #{color} やで!"
  end
end

いい感じですね。しっかり使い分けることができました。
クラスを定義する上で扱う変数には気を配らないと訳がわからなくなるので、こういったところから気をつけようと思います。

Effective Ruby

Effective Ruby