Why does this freeze not work?

hello,

i need to set an instance variable in my object once and make sure this
variable is not changed afterwards. i tried the code below which i
expected
to fail with a TypeError but it happily outputs ‘2’. i kind of
understand
why it succeeds, i think it just creates another @id when i call id=()
method, but is there a way to do what i want to do?

thanks
konstantin

class C
attr_accessor :id

def initialize(id)
@id = id
@id.freeze
end
end

c = C.new(1)
c.id = 2

puts c.id

Hi,

In message “Re: why does this freeze not work?”
on Thu, 16 Feb 2006 09:48:28 +0900, “konsu” [email protected]
writes:

|i need to set an instance variable in my object once and make sure this
|variable is not changed afterwards. i tried the code below which i expected
|to fail with a TypeError but it happily outputs ‘2’. i kind of understand
|why it succeeds, i think it just creates another @id when i call id=()
|method, but is there a way to do what i want to do?

No. You have frozen a Fixnum object 1, not @id. Instance variables
are attributes of an object, not objects by themselves. You have to
freeze whole object C, or use attr_reader instead of attr_accessor.

						matz.

Freezing affects the object on which you call the ‘freeze’ method, not
the context in which it’s assigned to a name. In your example, you’re
simply freezing a Fixnum instance representing the number 1. Then, when
you assign a new value to the ‘id’ instance attribute, you’re changing
which object it points to, rather than overwriting the original object.

This is because variables in Ruby are references to objects, not chunks
of memory. Object mutation happens via method call, not assignment.

If you were to freeze your instance of C, you would get behavior more
like you expect:

c = C.new(1)
c.freeze
c.id = 2 # raises TypeError

If you only want to prevent the ‘id’ attribute from being overwritten,
just declare it with ‘attr_reader’, instead of ‘attr_accessor’.

-Lennon

DÅ?a Å tvrtok 16 Február 2006 02:38 konsu napísal:

thank you. i understand.

then attr_accessor seems to be the only way. but one can always re-open my
object and change the @id attribute… that is why i wanted to freeze the
reference variable and not the object itself.

The same person could also reopen your class, replace #initialize_copy,
and
then dup your frozen object and play with it to his heart’s content.

If you design your object to be immutable, odds are noone will reopen
and hack
at it just to spite your design. And if it does happen, it probably
means
there’s something missing your API.

David V.

konsu wrote:

thank you. i understand.

then attr_accessor seems to be the only way. but one can always
re-open my object and change the @id attribute… that is why i
wanted to freeze the reference variable and not the object itself.

In your case simply change it to attr_reader. That way the field will
be
set in the constructor and cannot be changed from the outside. No
freezer
needed.

robert

thank you. i understand.

then attr_accessor seems to be the only way. but one can always re-open
my
object and change the @id attribute… that is why i wanted to freeze
the
reference variable and not the object itself.

konstantin

“rcoder” [email protected] wrote in message
news:[email protected]