【Ruby】メソッドの引数は参照の値渡し?

Ruby勉強中ですが、メソッドの引数の渡し方が意外とわかりにくかったので残します。 基本形。

def sample(num, str)
    puts num # => 1
    puts str # => hello!
end

num, str = 1, "hello!"
sample(num, str)

puts num # => 1
puts str # => hello!

Rubyは値渡し

メソッド内で値を書き換えても元の変数は書き換わらない。

def sample(num, str)
    num, str = 5, "world!"
    puts num # => 5
    puts str # => world!
end

num, str = 1, "hello!"
sample(num, str)

puts num # => 1  # 変わらない
puts str # => hello!  # 変わらない

ただし

破壊的メソッドを実行すると元の変数の値が変わってしまう。これは値渡しといいながらRuby自体は全てがオブジェクトであり、オブジェクトとしてメソッドに引数を渡すためこうなるらしい。

def sample(num, str)
    str.upcase!
    puts num # => 1
    puts str # => HELLO!
end

num, str = 1, "hello!"
sample(num, str)

puts num # => 1
puts str # => HELLO!  # 変わった!

メソッドに渡す前と後で、変数の領域は異なるが同じオブジェクトを参照してる。 だから「参照の値渡し」というみたい。ふーん。

HashとかArrayも書き換わってしまう

def sample(nums, strs)
    nums[:one] = 5
    strs[0] = "goodby!"
    puts nums # => {:one=>5, :two=>2}
    puts strs # => ["goodby!", "world!"]
end

nums = {one:1, two:2}
strs = ["hello!", "world!"]
sample(nums, strs)

puts nums # => {:one=>5, :two=>2}   # 変わった!
puts strs # => ["goodby!", "world!"]  # 変わった!

でも変数自体に入れなおした場合は

def sample(nums, strs)
    nums = {three:3, four:4}
    strs = ["HELLO!", "WORLD!"]
    puts nums # => {:three=>3, :four=>4}
    puts strs # => ["HELLO!", "WORLD!"]
end

nums = {one:1, two:2}
strs = ["hello!", "world!"]
sample(nums, strs)

puts nums # => {:one=>1, :two=>2}  # 変わらない
puts strs # => ["hello!", "world!"]  # 変わらない

なるほど、そういうことか

def sample(nums, strs)
    nums = {three:3, four:4}
    strs = ["HELLO!", "WORLD!"]
end

とした時点で引数(変数)の参照先が変わるから、元のオブジェクト参照が切れて影響なくなるんだなー。なるほど、気を付けよう。

参考