Misc #13634
openNilClass is lying about respond_to?(:clone)
Description
I put this under "Misc", because I'm not sure, whether this is a bug, a feature request, or maybe a deliberate (but for me obscure) decision in language design:
NilClass (and Fixnum) do not support clone. That's fine. However,
nil.respons_to?(:clone) returns true.
This means that we can ask nil to clone itself (we don't get a NoMethod error), it's just trying to do so throws an exception.
I stumbled over this problem when I had an collection of objects of different types, and wanted to apply :clone to some of them. My code went approximately like this:
object = collection[key]
return object.respond_to?(:clone) ? object.clone : object
This doesn't work, if object is nil, true, false, a Symbol or a Fixnum, because all of them claim to respond to :clone.
Of course, there is a trivial workaround (I just have to rescue the exception), but I find this language design not really intuitive. I think there are two possibilites, how this can be made better:
(1) If we decide, that nil is not clonable (because there can be only one nil), then respond_to?(:clone) should IMHO simply be false.
(2) However, there might be even be a reason why :clone should be applicable. Note that the usual semantics of clone is to do a shallow copy (for instance, when we 'clone' a nested array). If we want to have a deep copy, the usual approach is Marshal.load(Marshal.dump(object)). Now the odd thing is that we can not "shallowly copy" nil, i.e. nil.clone is forbidden, but we can do a deep copy, i.e. Marshal.load(Marshal.dump(nil)) works. So, an alternative would be to have nil.clone simply return the identical object.
Both (1) seems to me a sound solution. The solution (2) has the drawback that we can't guarantee anymore that x.clone has a different object id than x, but is probably the behaviour a programmer would intuitively expect.
Updated by Hanmac (Hans Mackowiak) over 7 years ago
@rovf (Ronald Fischer): that was fixed between 2.3 and 2.4
i currently have 2.4.1 for testing, and in that:
1.clone
and nil.clone
just returns the object instead of TypeError