Issue #21963 has been updated by jhawthorn (John Hawthorn). Eregon (Benoit Daloze) wrote:
`Class#new`, `dup`, `clone` and `Marshal` always use the internal alloc function, because they guarantee to call `initialize`, `initialize_dup` or `initialize_clone`.
Users have control over `initialize`, `initialize_dup` or `initialize_clone`. What's to stop them from replacing those methods with a no-op? On your branch: ```
RUBY_DESCRIPTION => "ruby 4.1.0dev (2026-03-24T15:12:19Z internal_alloc_fun.. b3a027d207) +PRISM [x86_64-linux]" match = "a".match(/./) => #<MatchData "a"> match.clone => #<MatchData "a"> def match.initialize_copy(x); end => :initialize_copy match.clone => #<MatchData:0x00007fd8a78022c0> # <- uninitialized match data
I thought about introducing a flag like this in #21267, but I just don't see a way that it guarantees the inability to create one of these uninitialized objects (rather than just making it slightly more difficult).
----------------------------------------
Feature #21963: A solution to completely avoid allocated-but-uninitialized objects
https://bugs.ruby-lang.org/issues/21963#change-116847
* Author: Eregon (Benoit Daloze)
* Status: Open
----------------------------------------
A common issue when defining a class is to handle allocated-but-uninitialized objects.
For example:
```ruby
obj = MyClass.allocate
obj.some_method
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 prevent `Class.instance_method(:allocate).bind_call(Foo)`. * `rb_undef_alloc_func()` but this breaks `dup`, `clone` and `Marshal`. 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`, `clone` and `Marshal` always use the internal alloc function, because they guarantee to call `initialize`, `initialize_dup` or `initialize_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#allocate` uses 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 as `rb_undef_alloc_func()`, so `Class#allocate` and `rb_get_alloc_func()` will raise if they are used (as they are unsafe). * Preserves the internal alloc function so `Class#new`, `dup`, `clone` and `Marshal` keep 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 -- https://bugs.ruby-lang.org/