Next: When an exception isn't handled, Previous: Raising exceptions, Up: Exception handling
To handle an exception when it occurs in a particular block of code,
use #on:do:
like this:
^[someText add: inputChar beforeIndex: i] on: ReadOnlyText do: [:sig | sig return: nil]
This code will put a handler for ReadOnlyText
signals on the
handler stack while the first block is executing. If such an exception
occurs, and it is not handled by any handlers closer to the point of
signalling on the stack (known as "inner handlers"), the exception object
will pass itself to the handler block given as the do:
argument.
You will almost always want to use this object to handle the exception somehow. There are six basic handler actions, all sent as messages to the exception object:
return:
Exit the block that received this #on:do:
, returning the given value.
You can also leave out the argument by sending #return
, in which case
it will be nil. If you want this handler to also handle exceptions in
whatever value you might provide, you should use #retryUsing:
with a
block instead.
retry
Acts sort of like a "goto" by restarting the first block. Obviously, this can lead to an infinite loop if you don’t fix the situation that caused the exception.
#retry
is a good way to implement reinvocation upon recovery,
because it does not increase the stack height. For example, this:
frobnicate: n [ ^[do some stuff with n] on: SomeError do: [:sig | sig return: (self frobnicate: n + 1)] ]
should be replaced with retry:
frobnicate: aNumber [ | n | n := aNumber. ^[do some stuff with n] on: SomeError do: [:sig | n := 1 + n. sig retry] ]
retryUsing:
Like #retry
, except that it effectively replaces the original
block with the one given as an argument.
pass
If you want to tell the exception to let an outer handler handle it,
use #pass
instead of #signal
. This is just like rethrowing
a caught exception in other languages.
resume:
This is the really interesting one. Instead of unwinding the stack,
this will effectively answer the argument from the #signal
send.
Code that sends #signal
to resumable exceptions can use this
value, or ignore it, and continue executing. You can also leave out
the argument, in which case the #signal
send will answer nil.
Exceptions that want to be resumable must register this capability by
answering true
from the #isResumable
method, which is
checked on every #resume:
send.
outer
This is like #pass
, but if an outer handler uses #resume:
,
this handler block will be resumed (and #outer
will answer the
argument given to #resume:
) rather than the piece of code that
sent #signal
in the first place.
None of these methods return to the invoking handler block except for
#outer
, and that only in certain cases described for it above.
Exceptions provide several more features; see the methods on the classes
Signal
and Exception
for the various things you can do
with them. Fortunately, the above methods can do what you want in almost
all cases.
If you don’t use one of these methods or another exception feature to exit
your handler, Smalltalk will assume that you meant to sig return:
whatever you answer from your handler block. We don’t recommend relying
on this; you should use an explicit sig return:
instead.
A quick shortcut to handling multiple exception types is the
ExceptionSet
, which allows you to have a single handler for the
exceptions of a union of classes:
^[do some stuff with n] on: SomeError, ReadOnlyError do: [:sig | ...]
In this code, any SomeError
or ReadOnlyError
signals will
be handled by the given handler block.
Next: When an exception isn't handled, Previous: Raising exceptions, Up: Exception handling