k-tokitoh

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

2019-07-05

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"の直後。

メモリ番地変数名オブジェクト番号中身
0x0000argobj47397028882260(への参照)
0x1000obj47397028882260(“hoge”という文字列オブジェクト)

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

メモリ番地変数名オブジェクト番号中身
0x0000argobj47397028882260(への参照)
0x0001paramobj47397028882260(への参照)
0x1000obj47397028882260(“hoge”という文字列オブジェクト)

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

メモリ番地変数名オブジェクト番号中身
0x0000argobj47397028882260(への参照)
0x0001paramobj47397028882260(への参照)
0x1000obj47397028882260(“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の直後。

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

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

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

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

メモリ番地変数名オブジェクト番号中身
0x0000argobj1111(への参照)
0x0001paramobj1131(への参照)
0x1000obj1111(555 という数値オブジェクト)
0x1001obj1131(565 という数値オブジェクト)

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

おわりに

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

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

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