I noticed some surprising behavior in one of my Ruby programs, and am
wondering what the rationale is. Basically,
given read-only access to a member variable (using attr_reader) of
class Array, I can modify that Array by modifying (what I think should
be) a local copy of it. Shouldn’t the accessor return a copy of the
variable instead of a reference to it? Or is it standard practice to
hand-write an accessor for arrays that returns a clone of the array?
#!/usr/bin/ruby
class Example
attr_reader :elems
def initialize
@elems = []
3.times { @elems << Object.new }
end
def to_s
@elems.inspect
end
end
e = Example.new
puts e
output is:
-604282308
list = e.elems
puts list.object_id
output is: -604282308
list.delete_at(0)
puts e
output is
-604282308
On Mon, Apr 14, 2008 at 1:25 PM, Adam B. [email protected] wrote:
class Example
end
list.delete_at(0)
puts e
output is
-604282308
You misunderstand what “attr_reader” does. The “attr_” methods simply
generate setter / getter methods on your class for instance variables.
attr_reader :variable
def variable; @variable; end
attr_writer :variable
def variable=(val); @variable = val; end
attr_accessor :variable creates both.
If you want something truely immutable, you have to #freeze it, but
then it’s not changeable inside the class either.
If you’re really worried about this, you can manually do:
class Example
def initialize
@elems = []
3.times { @elems << Object.new }
end
def to_s
@elems.inspect
end
Always give a copy of the array, that way the internal @elems
array is never changed.
def elems
@elems.clone
end
end
Hope that helps.
Jason R.
On Apr 14, 2008, at 11:34 AM, Jason R. wrote:
Always give a copy of the array, that way the internal @elems
array is never changed.
def elems
@elems.clone
end
end
does not work, neither does dup. both are rather shallow in ruby. if
you really want a copy you need
Marshal.load(Marshal.dump(@elems))
otherwise you’ll end up with subtle bugs when the array is cloned, but
elements inside of it, or deeper, are not.
fyi.
a @ http://codeforpeople.com/
On Apr 14, 11:25 am, Adam B. [email protected] wrote:
class Example
end
list.delete_at(0)
puts e
output is
-604282308
This was recently discussed on this list. See:
http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/18058b9f36bdd1a7/
But the short answer is: Yes, you’ll have to write your own accessor
if you don’t want this behavior. All attr_reader does is define the
accessor, which just returns the object. You don’t get a setter
method (elems=), but that doesn’t make the object immutable.
HTH,
Chris
On Apr 14, 2008, at 12:02 PM, Jason R. wrote:
Eep, yeah, I forgot about that. #dup and #clone won’t work, sorry for
that misinformation.
well they might
i’ve killed a lot of time over the years on that one ;-(
cheers.
a @ http://codeforpeople.com/
On Mon, Apr 14, 2008 at 1:43 PM, ara.t.howard [email protected]
wrote:
we can deny everything, except that we have the possibility of being
better. simply reflect on that.
h.h. the 14th dalai lama
Eep, yeah, I forgot about that. #dup and #clone won’t work, sorry for
that misinformation.
Jason