Search code examples
crystal-lang

How to do user input in Crystal


puts "Input a number"
A = gets.try(&.to_i) || 0
puts "Ok now another number"
B = gets.try(&.to_i) || 0

def main 
    puts "Value of multiplication is #{A} * #{B}, Which equals = #{A * B}"
    puts "Value of addition is #{A} + #{B}, Which equals = #{A + B}"
    puts "Value of division is #{A} / #{B}, Which equals = #{A / B}"
    puts "Value of minus is #{A} - #{B}, Which equals = #{A - B}"
    puts "Value of modulus is #{A} % #{B}, Which equals = #{A % B}"
end 

main

I am wondering what the most simplest way to get user inputs from integers in Crystal would be? Similar to Ruby's gets.chomp.to_i


Solution

  • To get user input you have to understand how gets works. It will return a String when the user inputs something, or nil when the user didn't input anything (for example the user pressed ctrl+C).

    In Ruby if you do:

    x = gets.chomp.to_i
    

    and run it, but press ctrl+C you get an ugly exception printed on the screen. Maybe that's acceptable, but it's something that Crystal won't less you pass that easily because Crystal is type safe and you will have to deal with this case. Maybe you want to print something nice to the user instead of an exception?

    Ways to deal with Nil are explained here: https://crystal-lang.org/api/0.25.1/Nil.html

    For example you can do:

    a = gets
    if a # if a is a String
      a = a.chomp.to_i # turn it into an integer
    else
      exit # just silently quit the program
    end
    

    Another way is to use to_nil!, which will essentially raise an exception in case the value is nil:

    a = gets.not_nil!.chomp.to_i
    

    That's more similar to Ruby, in that you'll get an exception shown in the console when the user quits with ctrl+C. The key difference is that Crystal is letting you know (through nil) that this possibility can happen and that you should choose what to do in this case.

    There are more ways to deal with this case (like the try solution suggested in other comments), depending on how terse you want the code to be.

    A final alternative which is closer to Ruby is to use read_line, which will raise in case there's no input, so you can just do:

    a = read_line.chomp.to_i
    

    (though in my experiment right now it doesn't seem to be raising, I have no idea why, probably some bug...)