Bug #11929
closedNo programatic way to check ability to dup/clone an object
Description
We can't dup a Fixnum, and it's ok. But I'm wondering «why is Fixnum saying it can dup?»
1.respond_to?(:dup) #=> true
Don't you think that the dup
method should be undefined in the class Fixnum?
Currently I can do class Fixnum; undef :dup; end
, but that should be in the core Ruby, isn't it?
Updated by 0x0dea (D.E. Akers) over 8 years ago
This demonstration should clarify the observed behavior. Symbol
, Fixnum
, and indeed every other numeric class inherit their #dup
from Kernel
, whose implementation does a sanity check before proceeding, thus obviating the need to remove the method from the classes for which it might otherwise make sense to do so.
Updated by MikeVastola (Mike Vastola) almost 8 years ago
- Subject changed from dup should be undefined in Fixnum to No programatic way to check ability to dup/clone an object
D.E., while you're technically not wrong, IMHO, the need to undef
the method is only abated in the strictest of senses: the sanity check averts what might otherwise be a segfault or memory leak or some other aberration, and makes things 'safe', but it's neither straightforward nor intuitive for the programmer, nor consistent with the implementation of similar functions in ruby.
For instance, freeze
doesn't throw any sort of similar error when you call it on an already frozen object, or even for the immediate (and therefore inherently immutable) objects you used in your demonstration.
I think the core issue Xavier is getting at here (and I took the liberty of updating the subject thusly; Xavier, if you don't feel it's a fair representation of the issue, please feel free to change it back) is that a select few object types in Ruby are immediate variables and therefore cannot be dup
ed/clone
d, yet there is no graceful/robust method of averting the error thrown by this sanity check when you attempt to dup
/clone
them. (In the source, both rb_obj_dup
and rb_obj_clone
if the object to be **dup
ed/clone
**d is a rb_special_const_p
. As far as I can tell, this method nor anything equivalent is exposed in the ruby language, however.)
I literally can't imagine any scenario in which a dev, when, say, coding a class with the line:
return val.dup.freeze
.. really wants an Exception
thrown when val
happens to be de-facto un-dup
-able. What they really want is:
return val.dup.freeze rescue val
To me it seems there are three possible solutions to this (of which any two can be implemented):
- As Xavier suggested, only define the functions for objects that will actually
dup
/clone
. (This will implicitly allowrespond_to?
checks as well as not interfere with existing code that may attempt torescue
failed attempts -- that is unlessTypeError
was specified, and even that could be worked around with a bit more code.) - Add additional methods to all objects named
dupable?
andclonable?
that return values consistent with exactly what their names suggest. - Have these methods function exactly the way
freeze
does: fail silently. If the object can't bedup
ed/clone
d because it's an immediate,dup
/clone
should return the object itself. (There shouldn't be any harm in doing so since nothing about the object can be changed in the first place.)
Updated by duerst (Martin Dürst) almost 8 years ago
- Assignee set to matz (Yukihiro Matsumoto)
I clearly prefer the last proposal (fail silently).
That would make for a much more unified, streamlined protocol, avoiding needless exposition of internals. It would do exactly what dup (and clone) are described to do, namely (pretend to) return a shallow copy.
Updated by Eregon (Benoit Daloze) almost 8 years ago
Martin Dürst wrote:
I clearly prefer the last proposal (fail silently).
That would make for a much more unified, streamlined protocol, avoiding needless exposition of internals. It would do exactly what dup (and clone) are described to do, namely (pretend to) return a shallow copy.
I very much agree.
Updated by lkdjiin (xavier nayrac) almost 8 years ago
Mike, your last proposal is simple, beautiful and smart.
1.dup #=> 1
Updated by matz (Yukihiro Matsumoto) over 7 years ago
- Status changed from Open to Feedback
- ability to be duped cannot be checked by
respond_to?
. It's simply wrong. - the "fail silently" proposal is attractive, but it should be a separate proposal (for the sake of bookkeeping).
Matz.
Updated by duerst (Martin Dürst) over 7 years ago
- Related to Feature #12979: Avoid exception for #dup on Integer (and similar cases) added
Updated by matz (Yukihiro Matsumoto) over 7 years ago
- Status changed from Feedback to Closed
The issue closed by #12979
Matz.