DEV Community

AW Unad
AW Unad

Posted on

Why class can mutate variable in Ruby?



class User
  def initialize(attr)
    @attr = attr
  end

  def add_name
    @attr[:name] = 'Foo Bar'
    @attr
  end
end

user = {}

User.new(user).add_name

user

=> {:name=>"Foo Bar"}

Top comments (5)

Collapse
 
rhymes profile image
rhymes

I'm not sure I understood what your question is.

In your example @attr is a hash, which is a mutable structure, when you call add_name the object containing the hash mutates it.

The line

User.new(user).add_name

is a shortcut for

o = User.new(user)
o.add_name

So you're not using the User class, but an instance (object) of such class. By calling add_name you modify the instance state, @attr

Collapse
 
awcodify profile image
AW Unad • Edited

it make sense if

user = {}
user = User.new(user).add_name

But why we have same result if do :

user = {}
User.new(user).add_name
Collapse
 
rhymes profile image
rhymes

Ah ok, I understand now, you're asking why the user variable is also mutated, not just @attr inside the object.

Ruby, same as Python, uses variables as containers to objects. In a sense everything is passed as a reference. If the referenced object (the hash) is mutable then such object can be mutated, and all the labels (the variables) that have a copy of that reference will point to the same object that has changed in the meantime.

Instead, if the object referenced by the variable is immutable, then the copy will updated to point to a new object.

For example:

[1] pry(main)> def dosomething(h)
[1] pry(main)*   h[:name] = "Hello"
[1] pry(main)* end
=> :dosomething
[2] pry(main)> o = {}
=> {}
[3] pry(main)> dosomething(o)
=> "Hello"
[4] pry(main)> o
=> {:name=>"Hello"}
[5] pry(main)> def donothing(a)
[5] pry(main)*   a = 4
[5] pry(main)* end
=> :donothing
[6] pry(main)> b = 5
=> 5
[7] pry(main)> donothing(b)
=> 4
[8] pry(main)> b
=> 5
[9] pry(main)>

as you can see the hash has been changed, but the variable with the number hasn't.

Thread Thread
 
awcodify profile image
AW Unad

Okay. Got it. So if we want to make it immutable, we have to copy it into new variable and modify on the new one.

Thanks!!

Thread Thread
 
rhymes profile image
rhymes

Yeah, you can clone it (beware of the difference between copy and deep copy) or you can freeze the object and make sure it can't be mutated