Henry Mazza
Henry Mazza

Reputation: 762

Ruby Nil and Zero

What's the science behind the fact that the to_i method in Ruby's NilClass instances returns zero? Returning nil or raising an exception would not be more logical?

Upvotes: 15

Views: 13752

Answers (6)

David Aldridge
David Aldridge

Reputation: 52376

A concise Ruby 2.3+ method for returning nil for nil.to_i instead of 0, is to use the safe navigation operator:

irb(main):001:0> nil.to_i
=> 0
irb(main):002:0> nil&.to_i
=> nil

Upvotes: 5

tokland
tokland

Reputation: 67870

It fits the Ruby philosophy of permissiveness (as opposed to, for example, the strictness of Python):

nil.to_i #=> 0 
"".to_i #=> 0
"123hello".to_i #=> 123
"hello".to_i #=> 0

As noted by Zabba, you can use Kernel#Integer(string) for strict conversion.

Upvotes: 16

dzuc
dzuc

Reputation: 760

If you happen to be in Rails then you can use try:

nil.to_i # => 0
nil.try :to_i # => nil

Upvotes: 6

Jörg W Mittag
Jörg W Mittag

Reputation: 369468

The protocol of to_i says that you must return an Integer and you must not raise an exception. Both of your suggestions violate at least one of those rules. So, no, those would not only not be more logical, they would be simply invalid.

Note, however, that nil does not respond to to_int. If it did respond to to_int, that would, indeed, be "illogical".

Upvotes: 6

DigitalRoss
DigitalRoss

Reputation: 146073

NilClass defines #to_i for the same reason it defines a #to_a that returns []. It's giving you something of the right type but an empty sort of value.

This is actually quite useful. For example:

<%= big.long.expr.nil? ? "" : big.long.expr %>

becomes:

<%= big.long.expr %>

Much nicer! (Erb is calling #to_s which, for nil, is "".) And:

if how.now.brown.cow && how.now.brown.cow[0]
  how.now.brown.cow[0]
else
  0
end

becomes:

how.now.brown.cow.to_a[0].to_i

The short conversions exist when only a representation is needed. The long conversions are the ones that the Ruby core methods call and they require something very close. Use them if you want a type check.

That is:

thing.to_int # only works when almost Integer already. NilClass throws NoMethodError

thing.to_i # this works for anything that cares to define a conversion

Upvotes: 14

Andrew Grimm
Andrew Grimm

Reputation: 81520

to_i means "convert me to an integer if you can".

If you want "if you're very much integer-like, give me your integer value, else give a NoMethodError", then use .to_int.

There's another question that asks about the difference between to_i and to_int, to_s versus to_str, etc. Let me know if you'd like me to find it for you.

Upvotes: 9

Related Questions