【初学者向け】Rubyにおける@nameとself.nameの違い　〜なぜ@なしで puts"#{name}"としてもちゃんと出力されるの？〜

## 概要

`@name`と`self.name`の挙動の違いをまとめます。
初学者の方向けの記事です。

以下のようなコードをたまに見かけることがありました。

```ruby
class User
  attr_reader :name
  def initialize(name) 
    @name = name
  end

  def introduce
    puts "#{name}です" #@がなくても参照できてる！
  end
end

user = User.new ("川上")
user.introduce
```
```zsh:出力結果
川上です
```
**「introduceメソッドの中の`name`はローカル変数なの？？`@`が付いてないのに，なぜ値が入っているの？」**
と思ってた謎が解けたので，まとめます。

## 結論

上記の`"#{name}です"`は，実は`"#{self.name}です"`の略。
`self.name`は，値を参照する`name`メソッド（ゲッターメソッド）を呼び出している。
`name`メソッド（ゲッターメソッド）の戻り値は`@name`なので，initializeメソッドで@nameに格納しておいた値が返る。
（なんのことかサッパリですよね^^; 詳しくは後述します）


それに対し，@nameはクラスの中で共有されるインスタンス変数。

同じ挙動をするように見えるけど，全くの別物。

## @name（インスタンス変数）を使用する書き方

まず，よく見るインスタンス変数を使った書き方から見てみます。

```rb:@nameを使用する書き方
class User

  def initialize(name)
    @name = name
  end
  
  def introduce
    puts "#{@name}です" 
  end
end

user = User.new ("川上")
user.introduce
```

```zsh:出力結果
川上です
```
インスタンスを生成した際，initializeメソッド内で@nameに実引数の値`川上`がセットされます。

クラスの中ではインスタンス変数の値が保持されるので，introduceメソッド内の@nameの値も`川上`です。

こちらは問題なさそうです。

## self.nameを使用する書き方
```rb:self.nameを使用する書き方
class User
  attr_accessor :name
  def initialize(name)
    self.name = name
  end
  
  def introduce
    puts "#{self.name}です" 
  end
end

user = User.new ("川上")
user.introduce
```
```zsh:出力結果
川上です
```

introduceメソッドの中だけでなく，initializeメソッドの中も`self.name`となっています。

すっかり忘れていたのですが，ProgateのRubyの演習ではこの形式で書かれているそうです。
（教えていただき見直してみると，確かにそうなっています。※2021年4月26日時点）

ここで注意したいのが，さりげなく**`attr_accessor :name`**と書かれている点です。
実は，この記述がない場合はエラーになってしまうんです。

もし，`attr_accessor :name`がなかったらどうなるか試してみます。

```rb:self.nameを使用する書き方
class User
  # attr_accessor :name # コメントアウト
  def initialize(name)
    self.name = name
  end
  
  def introduce
    puts "#{self.name}です" 
  end
end

user = User.new ("川上")
user.introduce
```
実行してみると，以下のエラーが出ました。（２つのうちの1つ目）

```zsh:出力結果
Traceback (most recent call last):
        2: from user.rb:12:in `<main>'
        1: from user.rb:12:in `new'
user.rb:4:in `initialize': undefined method `name=' for #<User:0x00007f7eec13c348> (NoMethodError)
```

`name=`メソッドが定義されていない，という内容ですね！

### self.name = はname=メソッド（セッターメソッド）を呼び出している

では，`name=`メソッドを追加してみましょう。

ちなみに`name=`メソッド（セッターメソッド）は何かというと，`@name`に値をセットするメソッドです。

```rb:self.nameを使用する書き方（セッターメソッドを追加）
class User

  def initialize(name)
    self.name = name # 実はセッターメソッドを呼び出している
  end
  # セッターメソッドを追加
  def name=(name)
    @name = name
  end
  
  def introduce
    puts "#{self.name}です" 
  end
end

user = User.new ("川上")
user.introduce
```
これでもう一度実行すると，上記のエラーに関してはなくなります。

実は，**initializeメソッドの中の`self.name =`は`name=`メソッド（セッターメソッド）を呼び出しています。**
（`self.name =`，`name=`と両方イコールがついている点に注意してください）

`name=`メソッドが定義されていないと，呼び出すことができないのでエラーになっていました。

`self.name =`が`name`（`川上`という値が入った変数)を実引数として`name=`メソッドを呼び出す
→`@name`に`川上`という値がセットされる，という流れです。

では，この状態でもう一度実行してみましょう。
すると，次は以下のエラー（２つ目のエラー）が出ます。

```zsh:出力結果
Traceback (most recent call last):
        1: from user.rb:17:in `<main>'
user.rb:12:in `introduce': undefined method `name' for #<User:0x00007fbcf209ff90 @name="川上"> (NoMethodError)
Did you mean?  name=
```
今度は`name`メソッドが定義されていない，という内容です。

### self.nameはnameメソッド（ゲッターメソッド）を呼び出している

では，`name`メソッド（ゲッターメソッド）を定義してみましょう。

`name`メソッド（ゲッターメソッド）は`@name`を戻り値とするメソッドです。

```rb:self.nameを使用する書き方（ゲッターメソッドを追加）
class User

  def initialize(name)
    self.name = name # 実はセッターメソッドを呼び出している
  end
  # セッターメソッド
  def name=(name)
    @name = name
  end

  # ゲッターメソッドを追加
  def name
    @name #戻り値
  end
  
  def introduce
    puts "#{self.name}です" # 実はゲッターメソッドを呼び出している
  end
end

user = User.new ("川上")
user.introduce
```
これでエラーはなくなります。

```zsh:出力結果
川上です
```
**introduceメソッドの中の`self.name`は`name`メソッド（ゲッターメソッド）を呼び出しています。**
（こちらはイコールなしです）

こちらも，メソッドが定義されていなかったので，呼び出すことができずエラーになっていました。

`self.name`が`name`メソッド（ゲッターメソッド）を呼び出す
→@nameに格納されていた`川上`という値が戻り値として`self.name`に入る，という流れです。



## セッターとゲッターをあわせてattr_accesorと書き換えることができる

まず，セッターメソッドは以下のように書き換えることができます。（`class User` の直下）

```rb:self.nameを使用する書き方（セッターメソッドを書き換え）
class User
  attr_writer :name # セッターメソッドを呼び出す
  def initialize(name)
    self.name = name
  end
  # セッターメソッド
  # def name=(name)
  #   @name = name
  # end

  # ゲッターメソッド
  def name
    @name
  end
  
  def introduce
    puts "#{self.name}です" 
  end
end

user = User.new ("川上")
user.introduce
```
（追記：コメントで教えていただいたので，「# 裏でセッターメソッドを呼び出す」という表記から「# セッターメソッドを呼び出す」という表記に変更いたしました！次のゲッターメソッドの書き換えについても同様です）

そして，ゲッターーメソッドは以下のように書くことができます。

```rb:self.nameを使用する書き方（ゲッターメソッドを書き換え）
class User
  attr_writer :name # セッターメソッドを呼び出す
  attr_reader :name # ゲッターメソッドを呼び出す
  def initialize(name)
    self.name = name
  end
  # セッターメソッド
  # def name=(name)
  #   @name = name
  # end

  # ゲッターメソッド
  # def name
  #   @name
  # end
  
  def introduce
    puts "#{self.name}です" 
  end
end

user = User.new ("川上")
user.introduce
```
`attr_writer :name`と`attr_reader :name`をあわせて`attr_accessor :name`と書くことができます。

これが「self.nameを使用したコード」に出てきた書き方になります。

```rb:self.nameを使用する書き方（attr_accessorに書き換え）
class User
  attr_accessor :name # セッターとゲッターを呼び出す
  def initialize(name)
    self.name = name
  end
  # セッターメソッド
  # def name=(name)
  #   @name = name
  # end

  # ゲッターメソッド
  # def name
  #   @name
  # end
  
  def introduce
    puts "#{self.name}です" 
  end
end

user = User.new ("川上")
user.introduce
```
```zsh:出力結果
川上です
```

## 【補足1】selfはインスタンス自身を表している

では，「この`self`って何？」というところなんですが，`self`は`user`というインスタンスを表しています。

クラスの外でインスタンスメソッドを呼び出す際，例えば

```ruby
user.introduce
```
というように，`インスタンス.インスタンスメソッド`という形で書きますが，クラスの中ではインスタンス部分を`self`（インスタンス自身）と書きます。

クラスの中で

```ruby
self.name
```
と書くと，`user`というインスタンス自身に対して`name`メソッドを呼び出していることになります。

言われてみれば「なるほど！」と納得できますが，なかなか見かけないので馴染みがありませんでした^^;

## 【補足2】`self.name`の`self.`は省略することができる

`self.name`の`self.`は，意味が曖昧にならない限り省略することができます。
ただし，`self.name =`の`self.`は省略することができません。（ローカル変数`name`への代入とみなされます）

```rb:self.を省略する書き方
class User
  attr_accessor :name
  def initialize(name) 
    self.name = name # self.を省略できない
  end
  
  def introduce
    puts "#{name}です" # self.を省略できる
  end
end

user = User.new ("川上")
user.introduce
```

ちなみに，私が疑問に思っていた冒頭のコードは，@nameへの値のセットはセッターを介さずに行い，値を参照する時だけゲッターを介している形でした。

```rb:値を参照する時だけself.nameを使用する書き方
class User
  attr_reader :name #ゲッターのみ
  def initialize(name) 
    @name = name
  end

  def introduce
    puts "#{name}です" # self.を省略している
  end
end

user = User.new ("川上")
user.introduce
```
謎が解けてスッキリ！

（ただ，この書き方をされている場合，意図してこうされているわけではなく，「`@`をつけ忘れているけど，他の目的で`attr_reader :name`と書いてるから偶然参照できている」ケースが多いのかな，と思いました）


## 【補足3】@nameとself.nameは全く異なるもの
とても極端でありえない例ですが，attr_readerを使わずに`name`メソッドを書くとき，メソッドの中が

```ruby
def name
  @name
end
```
ではなく，例えば以下のような値だった場合，

```ruby
def name
  1000
end
```
`self.name`で`name`メソッドを呼ぶと

```ruby
1000
```
が戻り値として返ります。

このように，純粋な挙動を見ると，`self.name`は単に「`name`メソッドを呼ぶ」という処理をするだけで，メソッドの中身は関係ありません。
（同様に，`self.name =`も，全く異なる名前のインスタンス変数（たとえば`@age`）に値をセットすることができてしまいます）

attr_accessorを使用すれば上記のようにメソッドの中身がおかしくなることはありませんが，ゲッターメソッド（もしくはセッターメソッド）を呼ぶ，という挙動自体は変わりません。

~~インスタンス変数を使用する場合は，`@name`を直接定義するし，一度定義されたら`@name`自身が値を保持します。~~（追記：コメント頂き，以下のように修正いたします）

@nameはメソッドを呼ぶものではなく，インスタンス変数です。

両者は全くの別物です。

## 【補足4】同じ使用法であれば，@nameを使用したほうが良い

クラスの中で共有する値をセットしたり参照する場合は，普通に`@name`を使用したほうが良いです。
`self.name =`で値をセットしたり`self.name`で値を取得する場合，それぞれセッター，ゲッターを呼び出す必要があり，その分の処理が増えるからです。

逆に，`self.name`を使用したほうが良いケースが思い浮かばず・・ありましたらぜひ教えていただけたら幸いです🙇‍♀️
追記：コメント欄にて`self.name =`を使用するケースを教えていただきました！ありがとうございます。

## 終わりに

以上，私自身が疑問に思っていた部分だったのでまとめました。
もし内容に間違いがありましたら，ご指摘いただけますと幸いです☺️

## 参考

- Progate RubyⅣ (有料部分なのでURL添付なし）
- ドットインストール Ruby入門　#20 アクセサを使ってみよう　（同上）
- やんばるエキスパート　講座