[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Mutable member variables -- surprising behavior

Adam Bender

4/14/2008 5:25:00 PM

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>]

5 Answers

Jason Roelofs

4/14/2008 5:34:00 PM

0

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.

Chris Shea

4/14/2008 5:43:00 PM

0

On Apr 14, 11:25 am, Adam Bender <aben...@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>]

This was recently discussed on this list. See:
http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/18058b9...

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

ara.t.howard

4/14/2008 5:43:00 PM

0


On Apr 14, 2008, at 11:34 AM, Jason Roelofs 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://codeforp...
--
we can deny everything, except that we have the possibility of being
better. simply reflect on that.
h.h. the 14th dalai lama




Jason Roelofs

4/14/2008 6:03:00 PM

0

On Mon, Apr 14, 2008 at 1:43 PM, ara.t.howard <ara.t.howard@gmail.com> wrote:
>
> On Apr 14, 2008, at 11:34 AM, Jason Roelofs 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://codeforp...
> --
> 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

ara.t.howard

4/14/2008 6:08:00 PM

0


On Apr 14, 2008, at 12:02 PM, Jason Roelofs 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://codeforp...
--
we can deny everything, except that we have the possibility of being
better. simply reflect on that.
h.h. the 14th dalai lama