Feature #21963
openA solution to completely avoid allocated-but-uninitialized objects
Description
A common issue when defining a class is to handle allocated-but-uninitialized objects.
For example:
This can easily segfault for classes defined in C and raise an unclear exception for classes defined in Ruby.
As a workaround many core (and non-core) classes add a check that they are initialized in every instance method.
This is suboptimal for performance and correctness, classes should not need to care about allocated-but-uninitialized objects.
Fundamentally, to solve this we need to guarantee that after the allocation function is used that either initialize, initialize_dup or initialize_clone is called.
And we can't guarantee that for Class#allocate.
The current workarounds are:
undef allocate, but this does not preventClass.instance_method(:allocate).bind_call(Foo).rb_undef_alloc_func()but this breaksdup,cloneandMarshal.
The idea is to have in addition of the public alloc function (in rb_classext_struct.as.class.allocator) an internal alloc function.
Then:
Class#new,dup,cloneandMarshalalways use the internal alloc function, because they guarantee to callinitialize,initialize_duporinitialize_clone.rb_define_alloc_func()sets both fields.rb_undef_alloc_func()sets both fields.rb_get_alloc_func()reads the public alloc function (unchanged)Class#allocateuses the public alloc function (unchanged)
We add a new method on Class, for example Class#safe_initialization, which:
- Sets the public alloc function to
UNDEF_ALLOC_FUNC, same asrb_undef_alloc_func(), soClass#allocateandrb_get_alloc_func()will raise if they are used (as they are unsafe). - Preserves the internal alloc function so
Class#new,dup,cloneandMarshalkeep working.
After that the class has fully safe intialization and does not need to worry about allocated-but-uninitialized objects anymore.
From https://bugs.ruby-lang.org/issues/21852#note-7