try#
Run a block and divert any exception it throws into a catch block,
with an optional finally block that always runs on the way out.
try/catch is Perl’s structured exception-handling syntax. The
try block executes; if any statement in it throws (via die,
an uncaught exception from a callee, or a runtime error), control
transfers to the catch block with the exception value bound to the
parenthesised lexical. If nothing throws, the catch block is
skipped. Unlike eval, a return inside try returns from the
enclosing sub, not from the block — try is control flow, not a
call frame.
The syntax must be enabled with a feature guard:
use feature 'try';
Synopsis#
try BLOCK catch (VAR) BLOCK
try BLOCK catch (VAR) BLOCK finally BLOCK
What you get back#
try is a statement, not an expression, but it yields the last
evaluated value of whichever branch ran — the try block on success,
or the catch block on failure. Used as the final statement of a
sub or a do block, that value becomes the result:
my $value = do {
try {
fetch_thing(@args);
}
catch ($e) {
warn "fetch failed: $e";
$DEFAULT;
}
};
Do not write return inside a try that is being used this way
— return exits the enclosing sub, bypassing the assignment.
A finally block’s final expression is discarded; it cannot
contribute to the value.
Global state it touches#
$@is not the mechanism here.try/catchbinds the exception to the lexical named incatch (VAR), not to$@. Code insidecatchsees whatever$@happened to hold before thetry; rely on the lexical.$_,$!, and other dynamic globals are untouched by the construct itself — only by whatever code runs inside the blocks.
The catch variable#
catch must be followed immediately by a parenthesised variable
declaration. my is implicit — write catch ($e), not
catch (my $e). The variable is lexical to the catch block and
holds the thrown value exactly as die delivered it: a string,
a blessed reference, or any scalar:
try {
risky();
}
catch ($e) {
if (ref $e && $e->isa('My::Exception')) {
$e->rethrow if $e->is_fatal;
warn $e->message;
}
else {
warn "plain die: $e";
}
}
There is no multi-catch dispatch on exception class — write the
classification yourself inside the single catch block.
finally#
An optional third block runs on every exit path from the
try/catch construct: normal fall-through, exception caught by
catch, exception rethrown from catch, or a non-local transfer
(return, last, next, redo,
goto) out of either block.
try {
open_resource();
use_resource();
}
catch ($e) {
warn "failed: $e";
}
finally {
close_resource();
}
finally is the right place for release code that must run no
matter how the protected section exits — file handles, locks,
restoring global state.
A finally block has restrictions the other two blocks do not:
No
return, nogoto, nolast/next/redo. Attempting any of these is a compile-time or runtime error.The final expression value is ignored —
finallycannot contribute to the construct’s yielded value.
finally remains experimental in Perl 5.42 and emits a warning
in the experimental::try category when used. try and catch
themselves were de-experimentalised in 5.40 and no longer warn.
Contrast with eval { ... } / $@#
The older pattern is still supported, but try/catch fixes three
long-standing pitfalls:
Pitfall |
|
|
|---|---|---|
|
real hazard, needs |
exception bound to a fresh lexical |
|
returns from the |
returns from the enclosing sub |
Distinguishing “no error” from “error was false” |
|
|
Use try for new code. Reach for eval EXPR only when you
actually need string-eval’s runtime compilation.
Examples#
Trap an exception and log it:
use feature 'try';
try {
my $x = call_a_function();
$x < 100 or die "too big: $x";
send_output($x);
}
catch ($e) {
warn "unable to output a value: $e";
}
print "finished\n";
Use try as an expression inside do to pick a fallback:
use feature 'try';
my $config = do {
try { load_config($path) }
catch ($e) { warn "using defaults: $e"; default_config() }
};
Release a resource unconditionally with finally:
use feature 'try';
no warnings 'experimental::try'; # finally is still experimental
my $lock = acquire_lock();
try {
do_critical_work();
}
catch ($e) {
warn "critical work failed: $e";
}
finally {
release_lock($lock);
}
Classify exceptions inside a single catch block:
use feature 'try';
try {
fetch_remote();
}
catch ($e) {
if (ref $e && $e->isa('Net::Timeout')) {
retry_later();
}
elsif (ref $e && $e->isa('Net::Auth')) {
die $e; # rethrow auth failures
}
else {
warn "unexpected: $e";
}
}
return inside try returns from the enclosing sub — not just the
block:
use feature 'try';
sub first_valid {
for my $path (@_) {
try {
return load($path); # exits first_valid on success
}
catch ($e) {
warn "skip $path: $e";
}
}
return; # all paths failed
}
Edge cases#
Feature guard required. Without
use feature 'try'(or ause v5.34or later bundle that enables it),tryis a plain identifier and the construct is a syntax error.catchis mandatory.try { ... }on its own is not legal — everytryneeds acatch. Usedeferorfinallyif you want cleanup without exception handling, paired with a trivialcatch ($e) { die $e }rethrow if you also want to let the exception propagate.Rethrowing. Inside
catch,die $erethrows. There is no implicit rethrow if you simply fall off the end ofcatch; doing so swallows the exception. Decide deliberately.$@is not cleared by enteringtryand is not set by a caught exception. Do not read$@insidecatchexpecting the thrown value — read the lexical.caller()does not seetry. Likewhileorforeach,tryis not a call frame.callerskips it, which is correct: sincereturnpropagates throughtry,tryhas no independent return semantics worth exposing to introspection.Loop controls pass through.
last,next, andredoinsidetryorcatchact on the enclosing loop, not on thetryconstruct. Afinallyblock will still run before the transfer completes.finallyrestrictions are enforced.return,goto, and loop controls insidefinallyare errors. If you need conditional cleanup that may abort, do it incatchor outside the construct.No
try-expression sugar beyond last-statement yield.tryis not a general expression; it can only produce a value when it is the final statement of a block that yields (do, a sub).my $x = try { ... } catch ($e) { ... };is a syntax error.
Differences from upstream#
Fully compatible with upstream Perl 5.42.
tryandcatchare non-experimental (as of 5.40) and emit no warnings.finallyis still experimental in 5.42 and emitsexperimental::trywarnings when used; silence withno warnings 'experimental::try'.
See also#
catch— the mandatory second block; receives the exception in a parenthesised lexicalfinally— optional third block for unconditional cleanup; still experimental in 5.42die— the throw side of the construct; any scalar (string, blessed ref, object) can be the thrown valueeval— the older block-eval /$@pattern, still needed for stringeval; prefertryfor pure exception handlingdefer— unconditional cleanup without a surroundingtry/catch; the semantic basis on whichfinallyis definedreturn— insidetryreturns from the enclosing sub, not from the block; insidefinallyit is an error