Rubyのクラスインスタンス変数と特異クラス

昼休みに先輩と雑談で話していた内容を自分の勉強がてらメモ
雑多なメモですが、参考になれば幸いです。

追記：scivolaさんのご指摘を受けて、一部変更しました。

#環境
Ruby 2.5.0

#クラスインスタンス変数
Rubyにはクラスインスタンス変数があります。
クラスインスタンス変数は下記のようなもの。

```ruby
class A
  @a = 1

  def instance_m
    @a
  end

  class << self
    def class_m
      @a
    end
  end
end
```

このように記述するとどうなるかというと、

```ruby
pry(main)> A.new.instance_m
=> nil
pry(main)> A.class_m
=> 1
```

と `class A` のインスタンスメソッドからはアクセスできないが、 `class A` のクラスメソッドからはアクセスできる変数となる。これがクラスインスタンス変数。

一般的には多分下記にように、クラスメソッドの中でクラスインスタンス変数を書くのが普通。逆にいうとクラスメソッドの中でインスタンス変数を定義するとクラスインスタンス変数となる。

```ruby
class A
  class << self
    def class_m
      @a = 1
    end
  end
end
```

#特異クラス
Rubyにおいてクラスメソッドは特異メソッドであり、公式ドキュメントにもそのように書かれている。
https://docs.ruby-lang.org/ja/latest/doc/spec=2fdef.html
(クラスメソッドの定義欄参照)

```
Ruby におけるクラスメソッドとはクラスの特異メソッドのことです。
Ruby では、クラスもオブジェクトなので、
普通のオブジェクトと同様に特異メソッドを定義できます。
```

よく知られている話だが、クラスメソッド（＝特異メソッド）には二つの定義方法がある。

```ruby
class B
  def self.hoge
    puts "hoge"
  end

  class << self
    def fuga
      puts "fuga"
    end
  end
end
```

このように定義すると。クラスメソッドが定義される。

```ruby
pry(main)> B.hoge
hoge
=> nil
pry(main)> B.fuga
fuga
=> nil
```

前者の方法は特異メソッドに直接定義する方法であり、後者の方法は特異クラスを開き、その中でメソッド定義をする方法となっている。どちらにせよクラスメソッドを定義することが可能であり、 `class << self` によって開かれているのが特異クラス。

#ここで疑問に思ったこと
それでは特異クラスの中でクラスインスタンス変数を定義するとどうなるのであろうか？試しに定義してみる。

```ruby
class C
  @c = 1
  class << self
    @c = 2
    def class_m
      @c
    end
  end
end
```

こうするとどうなるかというと…

```ruby
pry(main)> C.class_m
=> 1
```
当たり前と言えば当たり前なのだが、特異クラスで定義したクラスインスタンス変数は参照されず、 `class C` のクラスインスタンス変数 `@c = 1` が参照されている。

つまり、 `@c = 2` はクラスメソッドからもインスタンスメソッドからもアクセスできない存在となる。

#どうやってアクセスするのか？
上記のようなことは実務では使うことはほとんどないので問題がないと言えば問題がないのだが、やはり気になる…
それではどうやってアクセスするのかというと極めて簡単で下記のように記述する。

```ruby

class D
  @d = 1

  class << self
    @d = 2
    def class_m
      @d
    end

    class << self
      def singleton_class_m
        @d
      end
    end
  end
end
```

少し気持ち悪いが `class D` の特異クラスの中でさらに特異クラスを開き、その中で特異クラスメソッドを定義する…
こうすると下記のようになる。

```
pry(main)> D.class_m
=> 1
pry(main)> D.singleton_class.singleton_class_m
=> 2
```

まず先ほどと同じように `class D` のクラスメソッドから参照した場合は、 `class D` のクラスインスタンス変数 `@d = 1` が呼ばれることになる。

一方で `D.singleton_class` とするとDの特異クラスにアクセスすることができる。

 `class D` の特異クラスの中で、この特異クラスのクラスメソッドで呼び出すと、 `class D` の特異クラスのクラスインスタンス変数 `@d = 2` が呼ばれることになるのである！

#どういうことか
少し頭がこんがらがるかもしれないが、考えてみれば当たり前のことである。
最初にクラスインスタンス変数はクラスメソッドからはアクセスできるが、インスタンスメソッドからはアクセスできないと書いた。

つまりクラスインスタンス変数には、インスタンスメソッドからはアクセスできないのである。

再掲

```
class A
  @a = 1
  def instance_m
    @a
  end

  class << self
    def class_m
      @a
    end
  end
end
```

インスタンスメソッドからはクラスインスタンス変数はアクセスできない

```ruby
pry(main)> A.new.instance_m
=> nil
```

今回は特異クラスの中でクラスインスタンス変数を定義した。つまり特異クラスのインスタンスメソッド = 元のクラス（今回は `class D` ）のクラスメソッドになるので、当然、特異クラスのインスタンスメソッド（ `class D` のクラスメソッド）は、特異クラスのクラスインスタンス変数にはアクセスできない。

つまり `class D` のインスタンスメソッドが　`class D` のクラスインスタンス変数にアクセスできないのと全く同じである。

```ruby
class A
  @a = 1          # ここにはインスタンスメソッドからはアクセスできない。
  def instance_m
    @a            # インスタンス変数が定義されてないのでnilが返る
  end

  class << self
    def class_m
      @a          # クラスメソッドからは@a = 1にアクセスできる。
    end
  end
end
```

特異クラスのクラスインスタンス変数にアクセスするには特異クラスのクラスメソッドを定義する必要があるのである。そのため、特異クラスの中でさらに特異クラス（特異特異クラス？）を開き、 `class D` の特異クラスにクラスメソッドを定義することによってアクセスしたということである。

#終わりに
特異クラスと特異メソッドについて、しっかりと理解できていなかったが、今回の試行錯誤を通じて少しは理解できた気がする…
Rubyは奥深かくて楽しいので、初心者エンジニアとして少しずつ学んでいきたいと思います。
初心者なので、理解が間違っている点やわかりにくい点がありましたら、ご指摘頂けると大変ありがたく思います。

#参考
[メタプログラミングRuby](https://www.amazon.co.jp/dp/4873117437?linkCode=ll2&tag=teppeit-22&linkId=9d4eda4ede79b9465a43753f1b697272)

「初めてのRuby」を書かれたyuguiさんの記事
Rubyのメタクラス階層
http://blog.yugui.jp/entry/768

Ruby 初級者のための class << self の話 (または特異クラスとメタクラス)
https://magazine.rubyist.net/articles/0046/0046-SingletonClassForBeginners.html