catch#
Handle an exception thrown by a preceding try block.
catch is the second half of the try/catch construct. It is
not a standalone statement: it follows a try block, declares a
lexical VAR to receive the exception value, and runs its BLOCK
only when the try block died. If the try block completed
normally the catch block is skipped. Control then resumes at the
statement following the whole construct (or the finally block,
if one is present).
This syntax must be enabled with use feature 'try'. try and
catch are non-experimental as of Perl 5.40.
Synopsis#
try BLOCK catch (VAR) BLOCK
try BLOCK catch (VAR) BLOCK finally BLOCK
What you get back#
catch is not a function call and has no return in the expression
sense. Like other control-flow constructs, the value of the whole
try/catch is the value of whichever block ran last —
useful inside a do block or as the tail expression of a
subroutine:
my $value = do {
try {
fetch_thing(@args);
}
catch ($e) {
warn "fetch failed: $e";
$DEFAULT;
}
};
If try ran to completion, the construct yields the try block’s
last value and VAR is never bound. If try died, the construct
yields the catch block’s last value.
The VAR declaration#
The parentheses after catch are mandatory and must contain exactly
one scalar variable declaration. It behaves like a my declaration
— the my keyword is implicit, as in subroutine signatures:
catch ($e) { ... } # $e is a new lexical
catch (my $e) { ... } # syntax error — 'my' is already implied
VAR is scoped to the catch block only. It is not visible in the
preceding try block, in any following finally block, or
after the construct. Its value is whatever the try block threw —
a string, a list that stringified into one, or a reference, typically
a blessed exception object. See die for how the exception value
is produced.
Unlike eval, catch does not read or clear $@; the
exception travels through VAR directly. $@ is untouched by
the construct.
Global state it touches#
catch itself touches no special variables. The try/catch
construct as a whole leaves $@ unchanged — a deliberate
divergence from eval intended to stop callers from conflating
the two idioms. If you need $@-style inspection, use
eval instead.
Examples#
Catch a simple string exception and report it:
use feature 'try';
try {
risky();
}
catch ($e) {
warn "risky failed: $e";
}
Inspect a structured exception object:
try {
process($record);
}
catch ($e) {
if (ref($e) && $e->isa('My::NotFound')) {
return; # missing is fine
}
die $e; # re-throw everything else
}
Re-throw unchanged with die. Because catch does not touch
$@, you re-throw the variable you bound, not $@:
try {
step();
}
catch ($e) {
log_error($e);
die $e; # propagates to the next handler
}
Produce a value from the construct without return. Using
return inside try or catch returns from the enclosing
subroutine, not from the construct — see Edge cases:
sub safe_parse {
my ($text) = @_;
try {
parse($text);
}
catch ($e) {
warn "parse error: $e";
undef; # value of the catch block
}
}
A catch block that itself dies — the new exception propagates
outward to the next enclosing handler (try/catch,
eval, or program exit):
try {
open_db();
}
catch ($e) {
cleanup();
die "open_db failed: $e"; # thrown from inside catch
}
Edge cases#
Cannot stand alone.
catch (VAR) { ... }without a precedingtryblock is a syntax error.catchis parsed as part of thetryconstruct, not as an independent keyword.Exactly one scalar in parentheses. The parentheses are mandatory, must declare exactly one scalar, and the
mykeyword is implied.catch { ... }without parentheses is a syntax error.VARis block-scoped. It exists only inside thecatchblock. Afinallyblock that follows does not see it.Falsy exceptions still trigger the handler.
trydistinguishes “died” from “completed normally” by a flag set at unwind time, not by truthiness of the exception value.die 0,die "", anddie undefall enter thecatchblock;VARreceives the value as thrown (withdie’s usual coercion ofundefto"Died at ...").$@is not set. Code that reaches for$@inside acatchblock is reading the caller’s$@, not the exception just caught. UseVAR.A
catchblock may die. Exceptions raised from insidecatchare not re-caught by the same construct. They propagate to the next enclosing handler. This is the mechanism for re-throw (die $e) and for reporting cleanup failures from the handler itself.returninsidecatchreturns from the enclosing subroutine, not from thecatchblock. This differs fromevalBLOCK, wherereturnwould leave only theeval. To yield a value from the construct, use the block’s final expression instead ofreturn.Loop controls (
last,next,redo) insidecatchtarget an enclosing loop, not the construct. Thetry/catchconstruct is not itself a loop.caller()does not see thecatchframe. Becausecatchdoes not interceptreturn, it is invisible tocaller, likewhileorforeachare.Pairs only with the immediately preceding
try. You cannot have twocatchblocks, nor place statements betweentryandcatch. The optionalfinallyblock, if present, must come aftercatch.
Differences from upstream#
Fully compatible with upstream Perl 5.42.
See also#
try— the block whose failure thiscatchhandles; always paired with exactly onecatchfinally— optional third block that runs regardless of whether thetryblock died; cannotreturnor use loop controlsdie— produces the exception value thatVARreceives; use it insidecatchto re-throweval— older exception-handling idiom using$@; reach fortry/catchinstead in new code for clearer scoping and no$@footguns