How to handle errors and prevent script from crashing after

Hi all. I’m writing a script to make a job a little bit easier, and it may be something I share with others so I’m trying to put a bit more care into making it. I’m not very familiar with error handling and I’m struggling with the control flow. How do I get the script to retry the failed action instead of quitting the process entirely? Most of the code presented works, except for the part where try/catch comes in. When those are touched, the script breaks.

# radius_roll will be the approx. measured radius of the roll, measured before unrolling
# thickness_band will be the meausured thickness of the band in the direction of the groove, measured before unrolling; b in the archimedian spiral equation.
# tau represents the circle constant, where tau/2 = pi. Equal to one revolution of a point a point some distance from the origin
# n will be the number of coils on the roll
# length will be the calculated length of the roll
# radius_axle will be the radius of the hole in the center of the roll

#Things to consider: freshness of the roll, machine last used on, should probably spit out a short, dated report.

const tau = 2*π

trigger = false

# the math doesn't change, this is for redundancy.
println("Choose your units: inches (\") or centimeters (cm)\n")
ans = readline()
while trigger == false
	if ans == "\""
		println("Inches selected\n")
		global trigger = true
	elseif ans == "cm"
		println("Centimeters selected\n")
		global trigger = true
	else
		println("Please input a valid unit of measurement from the selection: inches (\") or centimeters (cm)\n")
		global ans = readline()
	end
end

println("What is the radius of the roll in $ans?\n")
try
	global radius_roll = parse(Float64, readline())
catch e0
	throw(error("Please input a valid number for the radius of the roll in $ans.\n"))
end

println("You input $radius_roll $ans. Is that correct? Y/N\n")
ans1 = uppercase(readline())

while ans1 != "Y"
	if ans1 == "Y" 
		break
	elseif ans1 == "N"
		println("What is the radius of the roll in $ans?\n")
		try 
			global radius_roll = parse(Float64, readline())
		catch e1
			throw(error("Please input a valid number for the radius of the roll in $ans.\n"))
		end
		println("You input $radius_roll $ans. Is that correct? Y/N\n")
		global ans1 = uppercase(readline())
	elseif ans1 != "Y" && ans1 != "N"
		println("Please input a valid answer: Y/N\n")
		global ans1 = uppercase(readline())
	end
end


The easiest way is to use tryparse instead, which returns nothing instead of throwing an error, meaning that you can use normal control flow like while instead of try/catch:

radius_roll = nothing
while radius_roll === nothing
    println("What is the radius of the roll in $ans?\n")
    radius_roll = tryparse(Float64, readline())
    if radius_roll === nothing
        println("Please input a valid number")
    end
end

There’s actually a bug here: if the input stream is closed, readline() will keep on returning nothing, and the loop will go forever. You can trigger this by pressing Ctrl-D at the terminal when prompted for the radius.

To fix this, check for this with eof:

radius_roll = nothing
while radius_roll === nothing
    println("What is the radius of the roll in $ans?\n")
    radius_roll = tryparse(Float64, readline())
    if eof(stdin)
        error("no radius given")
        # or do whatever you want
    end
    if radius_roll === nothing
        println("Please input a valid number")
    end
end

You can technically make it work with the original parse by wrapping it in a function and recursively calling itself:

function get_number()
    println("I can has number?")
    return try
        parse(Float64, readline())
    catch e
        eof(stdin) && rethrow()
        get_number()
    end
end
get_number()

That looks very simple, I like it. Is it better generally to try and reduce the problem to more simple control flow like this? When would try/catch be more appropriate?

It also occurs to me to ask: is it better code hygiene to wrap this sort of thing in a function? It seems like it might be, and I was assuming a function would be overkill for the task.

Using a while loop around the try block is usually the simplest approach. That way, if an error happens, you catch it, show a message, and continue the loop instead of exiting the script. Once the operation succeeds, you can break out of the loop. Keeping the retry logic outside the try block also makes the control flow much easier to follow.