Skip to main content
Engineering LibreTexts

12.4: Exception Handlers

  • Page ID
    43732
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    The general mechanism is provided by the message on:do:. It looks like this:

    aBlock on: exceptionClass do: handlerAction
    

    aBlock is the code that detects an abnormal situation and signals an exception; called the protected block. handlerAction is the block that is evaluated if an exception is signaled and called the exception handler. exceptionClass defines the class of exceptions that handlerAction will be asked to handle.

    The message on:do: returns the value of the receiver (the protected block) and when an error occurs it returns the value of the handlerAction block as illustrated by the following expressions:

    [1+2] on: ZeroDivide do: [:exception | 33]
        →    3
    
    [1/0] on: ZeroDivide do: [:exception | 33]
        →    33
    
    [1+2. 1+ 'kjhjkhjk'] on: ZeroDivide do: [:exception | 33]
        →    raise another Error
    

    The beauty of this mechanism lies in the fact that the protected block can be written in a straightforward way, without regard to any possible errors. A single exception handler is responsible for taking care of anything that may go wrong.

    Consider the following example where we want to copy the contents of one file to another. Although several file-related things could go wrong, with exception handling, we simply write a straight-line method, and define a single exception handler for the whole transaction:

    | source destination fromStream toStream |
    source := 'log.txt'.
    destination := 'log-backup.txt'.
    [ fromStream := FileStream oldFileNamed: (FileSystem workingDirectory / source).
        [ toStream := FileStream newFileNamed: (FileSystem workingDirectory / destination).
            [ toStream nextPutAll: fromStream contents ]
                ensure: [ toStream close ] ]
            ensure: [ fromStream close ] ]
        on: FileStreamException
        do: [ :ex | UIManager default inform: 'Copy failed -- ', ex description ].
    

    If any exception concerning FileStreams is raised, the handler block (the block after do:) is executed with the exception object as its argument. Our handler code alerts the user that the copy has failed, and delegates to the exception object ex the task of providing details about the error. Note the two nested uses of ensure: to make sure that the two file streams are closed, whether or not an exception occurs.

    It is important to understand that the block that is the receiver of the message on:do: defines the scope of the exception handler. This handler will be used only if the receiver (i.e., the protected block) has not completed. Once completed, the exception handler will not be used. Moreover, a handler is associated exclusively with the kind of exception specified as the first argument to on:do:. Thus, in the previous example, only a FileStreamException (or a more specific variant thereof) can be handled.

    A Buggy Solution. Study the following code and see why it is wrong.

    | source destination fromStream toStream |
    source := 'log.txt'.
    destination := 'log-backup.txt'.
        [ fromStream := FileStream oldFileNamed: (FileSystem workingDirectory / source).
        toStream := FileStream newFileNamed: (FileSystem workingDirectory / destination).
        toStream nextPutAll: fromStream contents ]
            on: FileStreamException
            do: [ :ex | UIManager default inform: 'Copy failed -- ', ex description ].
        fromStream ifNotNil: [fromStream close].
        toStream ifNotNil: [toStream close].
    

    If any exception other than FileStreamException happens, the files are not properly closed.


    This page titled 12.4: Exception Handlers is shared under a CC BY-SA 3.0 license and was authored, remixed, and/or curated by Alexandre Bergel, Damien Cassou, Stéphane Ducasse, Jannik Laval (Square Bracket Associates) via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.