Help us understand the problem. What is going on with this article?

[Ruby]参照の値渡しと破壊的メソッド

More than 1 year has passed since last update.

困ったこと

困ったことが起こりました。

sample.rb
a = "hello"
b = a
c = b.concat(" world")

puts a
puts c
ターミナル
$ ruby sample.rb
hello world
hello world

aは"hello"で、それと同じにしたbに"world"をくっつけたものをcにしたはずなのに、aとcが同じになってしまいました。

a は "hello"
c が "hello world"
でないと納得いきません。

それぞれのオブジェクトはどうなっているのか

原因を探るべくobject_idを調べてみました。

sample.rb
a = "hello"
puts a.object_id

b = a
puts b.object_id

c = b.concat(" world")
puts c.object_id

puts a
puts c
ターミナル
$ ruby sample.rb
70135239344220
70135239344220
70135239344220
hello world
hello world

なんと、すべて同じobject_idです。
問題の根源はこの辺にありそうです。

変数とは

冒頭で困った原因は、変数についての考え方にありました。
そもそも、変数について勘違いしていたようです。

事件はここで起きていました。

b = a

ここではてっきり、"hello"という文字列が b に代入されたものだと思っておりました。
しかし、代入されたのは……そもそも代入という言葉がよくなく、ここでは

「aはbに値("hello")の参照を渡した」

といったぐあいでした。
言い換えると、

「bもaと同じくobject_idが70135239344220である"hello"を参照するようになった」

ということです。
だから a と b のobject_idが同じだったのです。

これが参照の値渡し

変数というものは、値そのものではなく値を参照していただけだったのです。

破壊的メソッドとは

破壊的メソッドとは、参照元そのものを変えてしまうメソッドです。
今回でいうとobject_idが70135239344220である"hello"そのものを変えるということです。

今回は、文字列をくっつけるconcatメソッドが破壊的メソッドでした。

sample.rb
c = b.concat(" world")

ここではobject_idが70135239344220である"hello"に"world"をくっつけ、それを c にも参照させています。

よって、いままでの"hello"は"hello world"に変わり、それを a, b, c すべてが参照しているというシチュエーションが出来上がりました。

だから、a と c は共に同じ値だったのです。 

まとめ

Rubyの変数はほとんどすべてがオブジェクトを参照しており、そのオブジェクトが変わればそれを参照していたすべての変数が変わるということでした。

代入って言葉も、よくある値が箱にはいるようなイメージの説明も、すべてはわかりやすくするためで、実際のところ少し違うんですね。

オブジェクト指向の理解が少しだけ進んだような気がします。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away