銀行員からのRailsエンジニア

銀行員からのRailsエンジニア

銀行員から転身したサービス作りが大好きなRailsエンジニアのブログです。個人で開発したサービスをいくつか運営しており、今も新しいサービスを開発しています。転職して日々感じていること、個人開発サービス運営のことなどを等身大で書いていきます。

attr_accessor(attr_writer)でインスタンス変数をセットするときはselfを省略できない【Ruby】

こんにちは。

最近 メタプログラミングRuby を読んだので、印象に残ったところをブログにまとめています。

今回は、Rubyのアクセスメソッド attr_writer, attr_accessor でインスタンス変数をセットするときにレシーバ(self.)を省略できないことについてまとめました。

サンプルコード

かなりシンプルなこちらのコードを実行すると何が出力されるでしょうか?

class Sample
  attr_accessor :name

  def set_name
    name = 'デフォルト太郎'
  end
end

sample = Sample.new
sample.set_name
puts sample.name #=> ?

ほぼ同じ内容をTwitterのアンケート機能で聞いてみた結果がこちらです。

attr_accessor は Sample クラス内に以下のコードを自動で定義してくれるので、name= メソッドによってインスタンス変数がセットされて「デフォルト太郎」と出力されそうな気がしますが、実際は何も出力されません

def name
  @name
end

def name=
  @name = name
end

この予想に反した結果となっている原因は、set_name メソッドにあります。

「name = 'デフォルト太郎'」が、ローカル変数の name に値を代入しているのか、attr_accessor で生成された「name =」メソッドをレシーバを省略して呼び出しているかが Ruby に判断できずRuby では前者(ローカル変数への代入)を採用することになっています。

よって、set_name メソッドはローカル変数の name に値を代入しているだけとなり、sample.name は 定義されていない インスタンス変数 @name を参照しているため、何も出力されないという挙動になっていました。

set_name メソッドを以下のようにすれば「デフォルト太郎」と出力されるようになります。

def set_name
  self.name = 'デフォルト太郎'
end

先ほどは省略していたレシーバ(self.)を明記することで、「name =」メソッドを呼び出していることが明確になったためです。

また、アクセスメソッドを使用せず、インスタンス変数に直接値を入れる以下の形でも「デフォルト太郎」が出力される挙動にすることができます。

def set_name
  @name = 'デフォルト太郎'
end

おわりに

ここまでお読みいただきありがとうございます。

意図しない挙動を避けるためのコードを2種類紹介しましたが、実務のコードでは self. をつける形もインスタンス変数に直接値を入れる形も見かけるので、どちらを選ぶかは好みな気がします。

メタプログラミングRuby では、今回取り上げたような Ruby の小ネタ的な話も色々と紹介されており面白いのでおすすめです。

メタプログラミングRuby 第2版

メタプログラミングRuby 第2版

(追記)
メタプログラミングRubyを読んで印象に残ったところまとめの第2弾として「自己yield」についてまとめました。
是非合わせてご覧ください。
ysk-pro.hatenablog.com