From: "Eregon (Benoit Daloze) via ruby-core" Date: 2026-04-17T20:49:37+00:00 Subject: [ruby-core:125312] [Ruby Feature#21963] A solution to completely avoid allocated-but-uninitialized objects Issue #21963 has been updated by Eregon (Benoit Daloze). Thank you for reviewing this ticket. I agree with both points. I thought it would be nice-to-have to also have this for classes defined in Ruby but as you say the worst case is NoMethodError, and the specifics of the proposal are mostly focused on C classes anyway. --- I've been discussing with @jhawthorn and @byroot to find a good solution for this. One tricky part is handling of Marshal in case the class defines the `marshal_load` protocol (no problem if it defines `_load` as that controls the allocation and no problem if the class defines neither). In the `marshal_load` case Marshal needs to allocate an instance without giving any arguments (I think to support cyclic references), and so it is hard to properly initialize the native state of that object in that case. I think we then have 3 choices: * Have a C hook called before `marshal_load`, to initialize the native state, but we still can't pass it argument so it doesn't seem helpful. * Accept segfault if users redefine `marshal_load` on a class defined in C (they should never do that), or somehow prevent that redefinition (I guess one could use `Class#freeze` for that maybe?) * Give up on the combination class defined in C + `marshal_load` and such classes much check if they are initialized in every instance method (like currently). Note that for classes defined in C in which the alloc func can define a safe native state without knowing the arguments (safe as in any instance method can work on such an uninitialized object) then this is not a problem. But that is not possible for some classes for which there is no usable "default/zero state". ---------------------------------------- Feature #21963: A solution to completely avoid allocated-but-uninitialized objects https://bugs.ruby-lang.org/issues/21963#change-117065 * 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/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/