Project

General

Profile

Bug #8078

Once nil is frozen, an unfrozen instance cannot be reached

Added by mhuggins (Matt Huggins) over 7 years ago. Updated over 7 years ago.

Status:
Rejected
Priority:
Normal
Assignee:
-
Target version:
-
ruby -v:
ruby 1.9.3p327 (2012-11-10) [i386-mingw32]
Backport:
[ruby-core:53343]

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) over 7 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) over 7 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? => 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.

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) over 7 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.

Also available in: Atom PDF