Rubyにおける(オブジェクトへの参照の)値渡しを理解しようとして無知を感じた

Rubyのメソッド呼び出しではだいたい全部(オブジェクトへの参照を)値渡しする。

ミュータブルなオブジェクトの場合

def method(param)
  p param            # => "hoge"
  p param.object_id  # => 47397028882260
  param.upcase!
  p param            # => "HOGE"
  p param.object_id  # => 47397028882260
end

arg = "hoge"
p arg            # => "hoge"
p arg.object_id  # => 47397028882260
method(arg)
p arg            # => "HOGE"
p arg.object_id  # => 47397028882260

メモリの状態を追っていく。

まずarg = "hoge"の直後。

メモリ番地 変数名 オブジェクト番号 中身
0x0000 arg obj47397028882260(への参照)
0x1000 obj47397028882260 ("hoge"という文字列オブジェクト)

method(arg)でメソッド内に入った直後。paramをつくり、オブジェクトへの参照を値渡ししている点に注意。

メモリ番地 変数名 オブジェクト番号 中身
0x0000 arg obj47397028882260(への参照)
0x0001 param obj47397028882260(への参照)
0x1000 obj47397028882260 ("hoge"という文字列オブジェクト)

param.upcase!の直後。文字列オブジェクトはミュータブルなので、既存のオブジェクトが書き換えられる。

メモリ番地 変数名 オブジェクト番号 中身
0x0000 arg obj47397028882260(への参照)
0x0001 param obj47397028882260(への参照)
0x1000 obj47397028882260 ("HOGE"という文字列オブジェクト)

なので、メソッドからでた後にargは"HOGE"を返す。

イミュータブルなオブジェクトの場合

def method(param)
  p param            # => 555
  p param.object_id  # => 1111
  param = param + 10
  p param            # => 565
  p param.object_id  # => 1131
end

arg = 555
p arg            # => 555
arg.object_id  # => 1111
method(arg)
p arg            # => 555
p arg.object_id  # => 1111

arg = 555の直後。

メモリ番地 変数名 オブジェクト番号 中身
0x0000 arg obj1111(への参照)
0x1000 obj1111 (555という数値オブジェクト)

method(arg)でメソッド内に入った直後。paramをつくり、オブジェクトへの参照を値渡ししている点に注意。

メモリ番地 変数名 オブジェクト番号 中身
0x0000 arg obj1111(への参照)
0x0001 param obj1111(への参照)
0x1000 obj1111 (555という数値オブジェクト)

param = param + 10の直後。数値オブジェクトはイミュータブルなので、既存の数値オブジェクトはそのままに、新しい数値オブジェクトがつくられる。

メモリ番地 変数名 オブジェクト番号 中身
0x0000 arg obj1111(への参照)
0x0001 param obj1131(への参照)
0x1000 obj1111 (555という数値オブジェクト)
0x1001 obj1131 (565という数値オブジェクト)

なので、メソッドからでた後にargは555を返す。

おわりに

参考:値渡しと参照渡しの違いを理解する

なんかまとめたけど、やっぱ「Rubyのオブジェクトがメモリ上でどう表現されるのか」とか全然わかってないから大事なことをごまかしたまま、イメージ図でふんわり理解したフリをしているだけだな。以下の記事などを理解できたらよさそう。

Ruby におけるメモリの話 - Qiita