为什么在将用户输入转换为整数时 Crystal 中需要 .to_s 方法?

Why is the .to_s method needed in Crystal when converting user input to an integer?

我刚开始 Crystal,但我 运行 遇到了一些我不明白的事情。我写了一个简单的程序来演示,它从控制台获取一个数字并添加一个。

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}"

如您所见,程序几乎相同,但是,在 crystal 中,我必须从控制台获取输入并将其转换为字符串,然后才能将其转换为整数。

我想知道的是:

这似乎是一个基本问题,但 crystal 还处于早期阶段,而且文档也很少。

错误

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
            ^

在大多数情况下,gets 将 return 一个字符串,但它也可能 returns nil

这在 Ruby 中不是问题,因为在您的示例中,您将在运行时 nil returned,即使您有,也有 NilClass#to_i 在 Ruby 中总是 returns 0.

但是 Crystal 编译器会预先检查对象类型,因此确保您的代码可以处理所有可能的 return 类型。不幸的是,在 Crystal 中,Nil 上还没有 to_i 方法,因此您会得到编译器错误:

undefined method 'to_i' for Nil (compile-time type is (String | Nil))

当运行任一程序时,尝试将 Ctrl+D (EOF) 作为输入。程序是否按预期运行?

Crystal 让您处理方法可能 return 的所有可能类型,从而保护您免受此类编程错误的侵害。例如,该程序的更正确版本可能是:

print "Enter a number: "

number = gets.try &.to_i?
if number
  puts "You entered #{number}. #{number} + 1 = #{number + 1}"
else
  puts "Please enter a valid number"
end

注意:String#to_i? returns nil 当转换失败时,而不是像 String#to_i 那样引发异常。