Feature #21126
closedDrop default_proc when Hash#freeze is called for better Ractor support
Description
Hash instances with default_proc set cannot be sent/moved across Ractors, even if they are frozen.
Consider the following code. Using a default proc to set an empty Array is a very common pattern, even introduced in the docs.
h = Hash.new {|h, k| h[k] = [] }
h[:foo] << 1
h.freeze
Ractor.new(h) {|h| p h }.take # <internal:ractor>:282:in 'Ractor.new': allocator undefined for Proc (TypeError)
https://docs.ruby-lang.org/en/3.4/Hash.html#class-Hash-label-Default+Proc
One must explicitly call h.default_proc = nil
before sending the hash to another Ractor.
This isn't the most friendly way for the programmer since (1) it is not easy to spot that the default hash is rendering the Hash unsendable, and (2) Hash#default_proc=
isn't a widely known API anyway (at least from my perspective).
Proposal¶
Automatically drop the default
default_proc
when Hash#freeze is called. They have little use after the Hash gets frozen.
Nevertheless, it should be pointed out that this is an incompatibility for Hash#default
Hash#default_proc
-- they currently return the original value, but they will return nil
.
Patch: https://github.com/ruby/ruby/pull/12717
Dropping the default_proc on Hash#freeze
will also be nicer for Ractor.make_shareable
users, since it does not require users to find the particular Hash with the default_proc buried somewhere.