はじめに
ある変数を別の変数に代入した後、代入後の変数に変更を加えると、元の変数まで変更が反映されてしまい、初めて知った挙動だったので、流れをまとめてみました。
概要
サンプルコードを以下に記します。
下記のように、クラス内で定義したADRESSという定数を、メソッド内で利用するために、adressという変数に格納しています。
class XXXX
ADRESS = {"東京都":["中央区","千代田区"],"大阪府":["大阪市","堺市"]}
def xxx
adress = ADRESS
...
adress.merge!({"愛知県":["名古屋市","豊田市"]}) ### adressに変更を加える
end
end
このxxxというメソッドを実行後、各変数の値を確認すると、下記のような結果となりました。
merge!を行ったadress の値が変更されたのは意図通りでしたが、元のADRESSの値まで変更が及んでいました。
puts adress
=> {:東京都=>["中央区", "千代田区"], :大阪府=>["大阪市", "堺市"], :愛知県=>["名古屋市", "豊田市"]}
puts ADRESS
=> {:東京都=>["中央区", "千代田区"], :大阪府=>["大阪市", "堺市"], :愛知県=>["名古屋市", "豊田市"]}
原因
原因について、調べていたところ、ちょうど、下記の記事が参考になりました。
記事の通り、各変数のobject_idを確かめてみると、同じ値を返ってきており、2つの変数が同じオブジェクトを参照していたことがわかりました。
### 同じオブジェクトを参照している
ADRESS.object_id
=> 17420
adress.object_id
=> 17420
このため、adressに変更を加えると、ADRESSにも変更が反映されてしまうということでした。
最後に
結構、長時間嵌ってしまいましたが、オブジェクトの振る舞いが確認できました。
あとは、定数が変化された時、エラーになってくれてたら、すぐ気づけたと思うので、定数の定義には、freezeとかした方がよさそう。