From: Joel VanderWerf Date: 2002-02-25T16:10:26+09:00 Subject: Re: Hash.each block parameters Sean O'Dell wrote: > > I ran into a problem where I should have called .each_key for a hash, but I > was calling .each and so I wrote it like this: > > myhash.each do | key | > end > > I couldn't get values out of the hash using the key during the block > iterations. nil was always returned. > > I studied my code awhile and realized I should be using each_key. Hash.each > provides TWO block parameters, not one like each_key (the key). I added the > second parameter and things were fine again. > > It did this very quietly, no error or anything...I just got nil values and > scratched my head until I figured it out. Is there a parameter to make Ruby > stricter towards this sort of thing? > > Sean You could freeze all your keys, and subclass Hash to raise on non-frozen keys. class FrozenKeyHash < Hash def []=(k,v) raise "Hell" unless k.frozen? super end def [](k) raise "Hell" unless k.frozen? super end end h = FrozenKeyHash.new h[[1,2].freeze] = 3 h[[4,5].freeze] = 9 p h[[1,2].freeze] # ==> 3 begin p h[[1,2]] rescue RuntimeError => e p e # ==> # end begin for key in h p h[key] # should be key[0], not key end rescue RuntimeError => e p e # ==> # end One problem: you can't freeze numbers and symbols. (Hey, matz, shouldn't numbers and symbols be considered as frozen anyway, since they are immutable?) But you could check for this case. Freezing hash keys has the added advantage of catching this kind of thing: x = "fred" h = {} h[x] = "wilma" x[/$/]= " flintstone" x #==> "fred flintstone" h[x] #==> nil But this doesn't seem to be a problem very much in practice. If you're using strings, then usually you want this behavior. If you're using your own objects, then hashing is (by default) based on object identity, and therefore unaffected by changing state, which is what you usually want. Ruby tends to handle each case in the the way you usually want. The one murky case is when hashes are used as keys--the hash value is based on identity rather than computed from the contents. Sometimes this is what you want, and sometimes not, but Ruby has to do it one way or ther other, and this way is computationally cheaper.