Exception handling

When executing a program, abnormal conditions can occur that force the Julia runtime to throw an exception or error, show the exception message and the line where it occurred, and then exit. For example (follow along with the code in Chapter 4errors.jl):

  • Using the wrong index for an array, for example, arr = [1,2,3]and then asking for arr[0] causes a program to stop with ERROR: BoundsError()
  • Calling sqrt() on a negative value, for example, sqrt(-3) causes ERROR: DomainError: sqrt will only return a complex result if called with a complex argument, try sqrt(complex(x)); the sqrt(complex(-3)) function gives the correct result 0.0 + 1.7320508075688772im
  • A syntax error in Julia code will usually result in LoadError

Similar to these, there are 18 predefined exceptions that Julia can generate (refer to http://docs.julialang.org/en/latest/manual/control-flow/#man-exception-handling). They are all derived from a base type, Exception.

How can you signal an error condition yourself? You can call one of the built-in exceptions by throwing such an exception; that is, calling the throw function with the exception as an argument. Suppose an input field, code, can only accept the codes listed in codes = ["AO", "ZD", "SG", "EZ"]. If code has the value, AR, the following test produces DomainError:

if code in codes 
    println("This is an acceptable code") 
else 
    throw(DomainError()) 
end

A rethrow() statement can be useful to hand the current exception to a higher calling code level.

Note that you can't give your own message as an argument to DomainError(). This is possible with the error(message) function (refer to the Conditional evaluation section) with a String message. This results in a program stopping with an ErrorException function and an ERROR: message message.

Creating user-defined exceptions can be done by deriving from the base type, Exception, such as mutable struct CustomException <: Exception end (for an explanation of <, refer to the The type hierarchy - subtypes and supertypes section in Chapter 6, More on Types, Methods, and Modules). These can also be used as arguments to be thrown.

In order to catch and handle possible exceptions yourself so that the program can continue to run, Julia uses the familiar try...catch...finally construct, which includes the following:

  • The dangerous code that comes in the try block
  • The catch block that stops the exception and allows you to react to the code that threw the exception

Here is an example:

a = [] 
try 
    pop!(a) 
catch ex 
    println(typeof(ex))   
    showerror(STDOUT, ex) 
end 

This example prints the output, as follows:

 ArgumentError 
     array must be non-empty 

Popping an empty array generates an exception. The variable, ex, contains the exception object, but a plain catch without a variable can also be used. The showerror function is a handy function; its first argument can be any I/O stream, so it could be a file.

To differentiate between the different types of exception in the catch block, you can use the following code:

try  
  # try this code 
catch ex 
  if isa(ex, DomainError) 
    # do this 
  elseif isa(ex, BoundsError) 
    # do this 
  end 
end 

Similar to if and while, try is an expression, so you can assign its return value to a variable. So, run the following code:

ret = try 
           global a = 4 * 2 
      catch ex 
      end 

After running the preceding code, ret contains the value 8.

Sometimes, it is useful to have a set of statements to be executed no matter what, for example, to clean up resources. Typical use cases are when reading from a file or a database. We want the file or the database connection to be closed after the execution, regardless of whether an error occurred while the file or database was being processed. This is achieved with the finally clause of a try...catch...finally construct, as in the following code snippet:

try
   global f = open("file1.txt") # returns an IOStream(<file file1.txt>) 
# operate on file f 
catch ex 
finally 
    close(f) 
end 

f must be defined as global in try, otherwise it is not known in the finally branch.

Here is a more concrete example:

try
open("file1.txt", "r") do f
k = 0
while(!eof(f))
a=readline(f)
println(a)
k += 1
end
println(" Number of lines in file: $k")
end
catch ex
finally
close(f)
end

The try...catch...finally full construct guarantees that the finally block is always executed, even when there is a return in try. In general, all three combinations of try...catch, try...finally, and try...catch...finally are possible.

It is important to realize that try...catch should not be used in performance bottlenecks, because the mechanism impedes performance. Whenever feasible, test a possible exception with normal conditional evaluation.

The preceding code can be written more idiomatically as follows:

open("file1.txt", "w") do f
# operate on file f
end

close(f) is no longer needed: it is done implicitly with the end.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.42.116