While many languages speak of "throwing" and "catching" errors, this isn't how Rebol uses the terms.
Instead, it's a generic way to move values up the stack. It gives you a handy "out" from control flow:
result: catch [
if condition [throw result]
some code
case [
condition [more code]
condition [more code, throw result]
]
additional code
throw result
]
The implementation of the feature is lightweight, and built on the same mechanic as RETURN. You could in fact use return to do this:
result: eval func [] [
if condition [return result]
some code
case [
condition [return code]
condition [more code, return result]
]
additional code
return result
]
You can, if you like, THROW an error... plain or raised...and CATCH it. But that's just because you can throw anything. Packs are fine, too:
>> [a b]: catch [
case [
1 = 2 [throw pack [10 20]]
1 = 1 [throw pack [100 200]]
]
]
== 100
>> a
== 100
>> b
== 200
So it's really about throwing whatever you like--not specific to errors or error handling.
THROW and CATCH are a great lightweight feature for control flow, that people really should be using more often than they do. (Ren-C uses "definitional throw", which means there's no risk of you calling a routine that would accidentally catch a throw that wasn't meant for it--which is quite important!)
Definitional Errors Use RAISE+TRAP
If you're dealing with definitional errors, then you RAISE them and then TRAP them.
>> trap [raise 'foo]
== make error! [
type: ~null~
id: 'foo
message: ~null~
near: '[raise 'foo **]
where: '[raise entrap trap eval catch* enrescue eval rescue console]
file: ~null~
line: 1
]
You can also use EXCEPT to trap with an infix construct:
>> (raise 'foo) except e -> [print e.id]
== foo
But by design, definitional errors must be triaged and handled immediately when they are returned--or the antiform error will "decay" into an "abrupt failure".
Abrupt Failures use FAIL+RESCUE
Generally speaking, it's not a good idea to react to abrupt failures (unless you are something like the CONSOLE, where all you are doing is reporting that the error happened.)
As mentioned above, a raised error will decay to an abrupt failure if it isn't triaged. But you can also cause an abrupt failure using FAIL.
(Fun tidbit: FAIL is implemented by raising a definitional error and then not triaging it before passing it on. Right now it passes it to NULL?, which doesn't use a meta-aware parameter convention so it forces decay to abrupt failure. fail: cascade [get $raise, get $null?]
)
To help emphasize that you should generally not be reaching for the RESCUE routine to recover from abrupt failures, it lives in sys.util.
>> sys.util/rescue [
foo: func [argument] [
return argment + 20 ; whoops, typo
]
foo 1000
]
== make error! [
type: 'Script
id: 'unassigned-attach
message: '[:arg1 "word is attached to a context, but unassigned"]
near: '[
return argment ** + 20]
where: '[foo enrescue eval rescue eval catch* enrescue eval rescue console]
file: ~null~
line: 2
arg1: 'argment
]
Hopefully it's clear to anyone--upon light reflection--why thinking you can handle abrupt failures is generally misguided!