Sunday, July 30, 2006

Ruby Array and Hash frozen behavior

While working through a problem recently with Michael Schubert we noticed some interesting behavior.

We had an array of strings and one of the strings was unexpectedly being changed. We decided to use freeze to help us determine when the string was being changed. Unortunately, a frozen array of strings does not prevent the strings from being changed.
irb(main):006:0> strings = %w[foo bar baz]
=> ["foo", "bar", "baz"]
irb(main):007:0> strings.freeze
=> ["foo", "bar", "baz"]
irb(main):011:0> strings[0] << 'oo'
=> "foooo"
irb(main):012:0> strings
=> ["foooo", "bar", "baz"]
To solve this problem you should simply freeze the object being changed instead of the object that contains reference to the object being changed.
irb(main):013:0> strings = %w[foo bar baz]
=> ["foo", "bar", "baz"]
irb(main):014:0> strings[0].freeze
=> "foo"
irb(main):015:0> strings[0] << 'oo'
TypeError: can't modify frozen string
from (irb):15:in `<<'
from (irb):15
from :0
This behavior also applies to hashes
irb(main):016:0> hash = { :foo=>'bar' }
=> {:foo=>"bar"}
irb(main):017:0> hash.freeze
=> {:foo=>"bar"}
irb(main):018:0> hash[:foo] << 'baz'
=> "barbaz"
irb(main):019:0> hash[:foo].freeze
=> "barbaz"
irb(main):020:0> hash[:foo] << 'baz'
TypeError: can't modify frozen string
from (irb):20:in `<<'
from (irb):20
from :0
For related information on OpenStruct, check out this previous entry.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.