Feature #16601


Let `nil.to_a` and `nil.to_h` return a fixed instance

Added by sawa (Tsuyoshi Sawada) almost 3 years ago. Updated almost 3 years ago.

Target version:


Now, nil.to_s returns a fixed instance:

nil.to_s.object_id # => 440
nil.to_s.object_id # => 440
nil.to_s.object_id # => 440

This is useful when we have some variable foo which may be either nil or a string, and we want to check its emptiness in a condition:

if foo.to_s.empty?; ... end

By this feature, we do not (need to) create a new instance of an empty string each time we check foo, even when it happens to be nil.

There are similar situations with arrays and hashes. We may have variable bar which may be either nil or an array, or baz which may be either nil or a hash, and we want to check their emptiness in conditions as follows:

if bar.to_a.empty?; ... end
if baz.to_h.empty?; ... end

But unlike nil.to_s, the methods nil.to_a and nil.to_h create new instances of empty array or hash each time they are called:

nil.to_a.object_id # => 540
nil.to_a.object_id # => 560
nil.to_a.object_id # => 580

nil.to_h.object_id # => 460
nil.to_h.object_id # => 480
nil.to_h.object_id # => 500

The fact that this is somewhat inefficient discourages the use of foo.to_a or foo.to_h in such use cases.

I request nil.to_a to nil.to_h to return a fixed empty instance.

Actions #1

Updated by sawa (Tsuyoshi Sawada) almost 3 years ago

  • Description updated (diff)

Updated by shevegen (Robert A. Heiler) almost 3 years ago

I somewhat agree with the explanation, so I think the suggestion in itself is fine
as such.

I believe there may be a language design consideration, though, e. g. whether matz
thinks that this makes sense from a language-design point of view (see the other
explanation about the various attr* methods and why there is no attr* variant
that e. g. combines reader methods with a trailing '?' character).

Another smaller issue, if it is an issue, may be for ruby newcomers. For example,
say that a new user comes to ruby and asks about nil.to_a and nil.to_h
specifically - will there be an explanation somewhere? I am really just thinking
about the new-ruby-user situation in this regard, e. g. they may like to
understand why .to_a and .to_h may be special but not other .to
* (if there
are any ... I don't even know how many exist for nil).

Note that these two points are not meaning that I am against the suggestion
at all - it is only meant to "carve out more details" from the proposal if
possible. :)

In my own code I usually check first for nil, before doing any further
checks, even "boolean checks" (if a variable is true or false). Not sure
if that is the best practice, different people write code differently,
but I sort of adopted that a long time ago. So I may not be the ideal
target audience either as I currently don't quite seem to do much on
nil, except actually I do indeed sometimes do .to_s, to ensure that
I have a string. This can indeed be a bit complicated sometimes, if
we have to distinguish between nil, string and a symbol. But I still
agree with your basic statement - it somewhat makes sense to me since
nil.to_a and nil.to_h will always "reproduce" the same result just
as .to_s would (and should, since it is nil).

Updated by byroot (Jean Boussier) almost 3 years ago

I'm not sure if nil.to_a and nil.to_h are actually frequent sources of allocations, but I agree with the general premise.

When profiling Ruby applications, it's frequent to see 30% of the time being spent in GC, which makes me think reducing useless allocations could lead to increased performance.

However as we've seen with trying to freeze the return of Symbol#to_s [#16150] , there are some backward compatibility concerns.

So maybe the priority would be to implement [#16153] so that this kind of changes can go through a normal deprecation cycle.


Also available in: Atom PDF