Why does the line, restaurant.menu << "pizza" , modify @menu attribute for our restaurant instance? The way I understand it is restaurant.menu is called and finishes evaluating to ["donuts", "cake"]. At this point the method call is done. So why is << pizza affecting the @menu attribute?
class Restaurant
def initialize(name)
@name = name
end
def menu
@menu = @menu || ["donuts", "cake"]
end
end
restaurant = Restaurant.new("Mcdonalds")
p restaurant.menu #["donuts", "cake"]
restaurant.menu << "pizza" #???
p restaurant.menu #["donuts", "cake", "pizza"]>
CodePudding user response:
As @red_menace suggests, it might be worthwhile trying something like:
class Restaurant
def initialize(name)
@name = name
@menu = ["donuts", "cake"]
end
def menu
@menu.dup
end
end
CodePudding user response:
Although @red_menace already answered what root cause is, but I will elaborate what that means.
First you need to understand Ruby objects mutability. Here you can find some good article.
Basically in your example menu
instance method of the class returns reference to instance variable @menu
of the class' instance, which is mutable array (you can modify that through the reference).
If you really want to make it immutable, you can either freeze the variable or clone it as it was suggested by @Jad.
Here you can find a great discussion on similar problem.
Here you can see an program trace that all the time you modify menu
method's returned result you modified instance variable @menu
.
require 'pp'
class Restaurant
def initialize(name)
@name = name
end
def menu
@menu = @menu || ["donuts", "cake"]
puts "@menu object_id = #{@menu.object_id}"
@menu
end
end
restaurant = Restaurant.new("Mcdonalds")
p restaurant.menu #["donuts", "cake"]
pp "object instance variables are #{restaurant.instance_variables}"
restaurant.menu << "pizza" #???
p restaurant.menu #["donuts", "cake", "pizza"]>
p "restaurand.menu.object_id = #{restaurant.menu.object_id}"
Here is the program's output:
$ruby main.rb
@menu object_id = 47245049576960
["donuts", "cake"]
"object instance variables are [:@name, :@menu]"
@menu object_id = 47245049576960
@menu object_id = 47245049576960
["donuts", "cake", "pizza"]
@menu object_id = 47245049576960
"restaurand.menu.object_id = 47245049576960"