Почему метод .to_s необходим в Crystal при преобразовании пользовательского ввода в целое число? - PullRequest
0 голосов
/ 02 марта 2019

Я начинаю с Кристалла и столкнулся с чем-то, чего не понимаю.Я написал простую программу для демонстрации, которая берет число из консоли и добавляет его.

Рубин

# Add one program.
puts "Enter a number."

number = gets

number = number.to_i

puts "You entered #{number}. #{number} + 1 = #{number + 1}"

Кристалл

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

Как видите, программы практически идентичны, однако в кристалле я должен взять ввод с консоли и преобразовать его в строку, прежде чем он может быть преобразован в целое число.

Что я хочу знать:

  • Что возвращается при получении в кристалле?
  • Есть ли другой способ сделать это без методов цепочки?

Может показаться, что это основной вопрос, но для кристалла еще рано, и документации немного.

Ошибка

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
            ^

Ответы [ 2 ]

0 голосов
/ 02 марта 2019

При запуске любой программы попробуйте ввести Ctrl + D (EOF) в качестве ввода.Программа ведет себя так, как вы ожидали?

Crystal защищает вас от подобных ошибок программирования, заставляя вас обрабатывать все возможные типы, которые может возвращать метод.Например, более правильной версией программы может быть:

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? возвращает nil при сбое преобразования, а не вызывает исключение, как String#to_i.

0 голосов
/ 02 марта 2019

В большинстве случаев gets возвращает строку, но возможно, что она также возвращает nil.

Это не проблема в Ruby, потому что в вашем примере вы когда-нибудь вернете nil во время выполнения, и даже если бы вы это сделали, в Ruby есть NilClass#to_i, который всегдавозвращает 0.

Но компилятор Crystal проверяет типы объектов заранее и поэтому гарантирует, что ваш код может обрабатывать все возможные типы возвращаемых данных.К сожалению, в Crystal нет метода to_i для Nil, и поэтому вы получаете ошибку компилятора:

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