Jason Roelofs
4/14/2008 5:34:00 PM
On Mon, Apr 14, 2008 at 1:25 PM, Adam Bender <abender@gmail.com> wrote:
> 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
> # [#<Object:0xb7f6c450>, #<Object:0xb7f6c43c>, #<Object:0xb7f6c428>]
>
> list = e.elems
> puts list.object_id
> # output is: -604282308
> list.delete_at(0)
>
> puts e
> # output is
> # -604282308
> # [#<Object:0xb7f6c43c>, #<Object:0xb7f6c428>]
>
>
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.