Eregon (Benoit Daloze) wrote in #note-1:
jeremyevans0 (Jeremy Evans) wrote:
I think this should allow extension libraries to start benefiting from core Set without having to resort to method calls (dangerous in an C extension as they could be redefined to return objects of an unexpected type).
What would be the danger with that?
It could cause a NoMethodError or so, but that's fine.
When you are using rb_funcall, you cannot rely on the type of returned objects. If you want to pass returned objects to other C-API functions, you need defensive type checks, or it can result in undefined behavior (including segfaults) depending on how the returned objects are used.
Let's say you want to call the Set#size
method and pass the result to rb_fix2str
. If a user overrides the Set#size
method to return an object of a different type, you get undefined behavior, as rb_fix2str
does not perform type checks. By having an rb_set_size
C-API function, you know what type will be returned.
Having C-API functions for common methods is more efficient performance wise, and increases programmer happiness, since calling the functions is simpler than using rb_funcall. This is especially true in the rb_set_foreach
case. Without rb_set_foreach
, instead of passing a C function pointer, you would have to build a Ruby block in C that you could pass to Set#each
or Set#delete_if
. Passing a C function is also more flexible, since the same function can deal with both iteration and deletion.
IMO rb_funcall() is enough for this, and anyway needs to be used for Ruby version < 3.5.
The idea that we shouldn't add this because it cannot be used on older Ruby versions is basically an argument against adding any feature at all. If we add this, code targeting Ruby 3.5+ can benefit from it, and eventually not working on versions older than Ruby 3.5 will be a non-issue.