Bug #8078
closedOnce nil is frozen, an unfrozen instance cannot be reached
Description
=begin
This is a rather peculiar bug I stumbled across. The basic issue is that once nil
has been frozen, there is no way to access an unfrozen instance of nil.
nil.frozen? => false
nil.freeze
nil.frozen? => true
I say this is a peculiar bug because generally it doesn't matter if nil
is frozen or not (since it is basically frozen even if not explicitly defined as such). However, since Ruby is based upon duck typing, you might run into a scenario like the following:
def get(cache_key)
content = Rails.cache.read(cache_key)
content.frozen? ? content.dup : content
end
If I call the above method and the cache key exists, content
is a frozen string, which I then dup
to get an unfrozen copy. If I call the above method and the cache key does NOT exist, content
is a frozen nil
, which it then attempts to dup
unsuccessfully since a NilClass instance cannot be duped.
=end
Updated by marcandre (Marc-Andre Lafortune) about 11 years ago
- Status changed from Open to Rejected
nil is an immediate. There are no multiple nils; nil.object_id is a constant. Same as true & false.
Any code that checks for frozen?
and then does a dup might have problems with any Ruby immediate. Note that fixnums & floats are frozen in Ruby 2.0.0. There is duplicable?
in activesupport for that reason.
Updated by kosaki (Motohiro KOSAKI) about 11 years ago
This is a rather peculiar bug I stumbled across. The basic issue is that once
nil
has been frozen, there is no way to access an unfrozen instance of nil.nil.frozen? => false
nil.freeze
nil.frozen? => trueI say this is a peculiar bug because generally it doesn't matter if
nil
is frozen or not (since it is basically frozen even if not explicitly defined as such). However, since Ruby is based upon duck typing, you might run into a scenario like the following:def get(cache_key)
content = Rails.cache.read(cache_key)
content.frozen? ? content.dup : content
endIf I call the above method and the cache key exists,
content
is a frozen string, which I thendup
to get an unfrozen copy. If I call the above method and the cache key does NOT exist,content
is a frozennil
, which it then attempts todup
unsuccessfully since a NilClass instance cannot be duped.
Hmm...
I don't think nil is mutable logically. Why can't we make nil is frozen at beginning?
Updated by marcandre (Marc-Andre Lafortune) about 11 years ago
kosaki (Motohiro KOSAKI) wrote:
I don't think nil is mutable logically. Why can't we make nil is frozen at beginning?
I can't see a really good use for it, but nil
can have attributes and is mutable.
Up to Ruby 2.0, the same was true for Fixnum, Bignum and Floats. But flonum optimization made it uncertain if a float was an immediate or not (e.g. 1.0 is an immediate in Ruby 2.0.0 on 64 bit platforms, but not if 32 bit). So it was decided to freeze them all, to avoid platform dependence. Since the same reasoning applied to Fixnum vs Bignum (e.g. 1<<42 is a Bignum on 32 bit platforms, so not an immediate, but is a Fixnum and thus an immediate on 64 bits), it was judged safer to freeze fixnums & bignums too.
On the other hand, nil
is always the same immediate, so there is no danger in leaving it unfrozen.