I'm getting started with Crystal, and I've run into something I don't understand. I've written a simple program to demonstrate, which takes a number from the console and adds one.
Ruby
# Add one program.
puts "Enter a number."
number = gets
number = number.to_i
puts "You entered #{number}. #{number} + 1 = #{number + 1}"
Crystal
# Add one program.
puts "Enter a number."
number = gets
number = number.to_s.to_i # Why is to_s needed?
puts "You entered #{number}. #{number} + 1 = #{number + 1}"
As you can see, the programs are nearly identical, however, in crystal I must take the input from the console and convert it to a string before it can be converted into an integer.
What I want to know is:
This may seem like a basic question, but it's still early days for crystal, and documentation is sparse.
Error
Error in example.cr:6: undefined method 'to_i' for Nil (compile-time type is (String | Nil)) (did you mean 'to_s'?)
number = number.to_i # Why is to_s needed?
^~~~
================================================================================
Nil trace:
example.cr:4
number = gets
^~~~~~
example.cr:4
number = gets
^~~~
/usr/share/crystal/src/kernel.cr:105
def gets(*args, **options)
/usr/share/crystal/src/kernel.cr:105
def gets(*args, **options)
^
/usr/share/crystal/src/kernel.cr:106
STDIN.gets(*args, **options)
^~~~
/usr/share/crystal/src/io.cr:574
def gets(chomp = true) : String?
/usr/share/crystal/src/io.cr:574
def gets(chomp = true) : String?
^
/usr/share/crystal/src/io.cr:574
def gets(chomp = true) : String?
/usr/share/crystal/src/io.cr:574
def gets(chomp = true) : String?
^~~~
/usr/share/crystal/src/io.cr:575
gets '\n', chomp: chomp
^~~~
/usr/share/crystal/src/io.cr:604
def gets(delimiter : Char, chomp = false) : String?
^~~~
/usr/share/crystal/src/io.cr:605
gets delimiter, Int32::MAX, chomp: chomp
^~~~
/usr/share/crystal/src/io.cr:618
def gets(delimiter : Char, limit : Int, chomp = false) : String?
^~~~
/usr/share/crystal/src/io.cr:619
raise ArgumentError.new "Negative limit" if limit < 0
^
/usr/share/crystal/src/io.cr:632
if ascii && !decoder && (peek = self.peek)
^
/usr/share/crystal/src/io.cr:633
if peek.empty?
^
/usr/share/crystal/src/io.cr:634
nil
^
In most cases gets
will return a String, but it is possible that it returns nil
too.
This isn't an issue in Ruby, because in your example you will ever have nil
returned at runtime and even if you had, there is NilClass#to_i
in Ruby which always returns 0
.
But the Crystal compiler checks object types upfront and therefore makes sure that your code can handle all possible return types. Unfortunately, in Crystal, there is no to_i
method on Nil
yet and therefore you get the compiler error:
undefined method 'to_i' for Nil (compile-time type is (String | Nil))