defer#
Schedule a block to run when the enclosing scope exits, for any reason.
defer is a flow-control keyword, not a function. Writing
defer { ... } inside a scope records the block for later execution;
when control leaves the enclosing block — by falling off the end, by
return, by die, by goto, or by a loop-control
statement (next, last, redo) — the stored
block runs on the way out. It is the standard idiom for pairing
acquisition with release (open/close, lock/unlock, push/pop) without
relying on object destructors or wrapping every path in
eval.
The feature is experimental and must be enabled. Either opt in explicitly or pick up the bundle:
use feature 'defer'; # explicit
use v5.36; # bundle — enables defer among others
Under use warnings 'experimental::defer' (or plain use warnings)
the first use emits an experimental warning. Silence it with
no warnings 'experimental::defer' once you have decided to rely on
the feature.
Synopsis#
defer BLOCK
defer takes a brace-delimited block and nothing else. There is no
expression form, no filehandle form, and no return value — the
statement exists for its side effect on scope exit.
What you get back#
defer is a statement, not an expression. It contributes no value to
its surrounding context. The block’s own return value is discarded
when it runs at scope exit.
When the deferred block runs#
The deferred block is invoked once, when control leaves the directly enclosing block, regardless of how control leaves:
normal fallthrough off the end of the block,
explicit
returnfrom the containing subroutine,an exception thrown by
dieor propagated from a called sub,gotoout of the block,
If the flow of execution never reaches the defer statement itself,
the block is not enqueued and does not run. This is the direct
opposite of an END phaser, which the compiler enqueues
unconditionally:
use feature 'defer';
{
defer { say "This will run"; }
return;
defer { say "This will not"; } # never reached, never scheduled
}
LIFO ordering#
Multiple defer blocks in the same scope run in last-in,
first-out order — the most recently scheduled block runs first.
This matches the natural nesting of resource acquisition: the last
thing acquired is the first thing released.
use feature 'defer';
{
defer { say "1"; }
defer { say "2"; }
defer { say "3"; }
}
# prints 3, then 2, then 1
Global state it touches#
None directly. The deferred block sees interpreter globals
($_, $!, $@, selected
filehandle, etc.) with whatever values they hold at the moment the
block runs, not at the moment it was scheduled. A defer that
wants to preserve $! or $@ across cleanup must save
and restore them itself — otherwise a system call inside the block
will clobber them and the caller of the enclosing sub will see the
overwritten values.
defer {
local ($!, $@);
close $fh; # clobbers $! on failure — local'd copy absorbs it
}
Examples#
Pair an open with its close, guaranteed to run even on die:
use feature 'defer';
sub read_config {
open my $fh, "<", "config.ini" or die "open: $!";
defer { close $fh; }
while (<$fh>) { ... }
# close $fh runs here on fallthrough, on return, and on die.
}
Unlock a shared resource on every exit path:
use feature 'defer';
sub critical_section {
$lock->acquire;
defer { $lock->release; }
do_work(); # release runs on fallthrough
return if $shortcut; # release runs before the return completes
die "boom" if $bad; # release runs, then the exception propagates
}
LIFO cleanup of nested resources — the outer resource is released last, which is usually what you want:
use feature 'defer';
sub copy_file {
open my $in, "<", $src or die "open $src: $!";
defer { close $in; }
open my $out, ">", $dst or die "open $dst: $!";
defer { close $out; }
print {$out} $_ while <$in>;
# close $out runs first, then close $in.
}
Temporarily adjust interpreter state and restore it on exit without
using local:
use feature 'defer';
sub with_separator {
my $saved = $,;
$, = " | ";
defer { $, = $saved; }
print @_, "\n";
}
Edge cases#
Conditional scheduling. A
deferstatement only schedules its block when control actually reaches it. An earlyreturnordiethat happens before thedeferline leaves nothing to run. Place thedeferimmediately after the paired acquisition so they cannot become separated by an intervening failure.Once scheduled, always runs. There is no mechanism to un-schedule a
deferblock. If the acquisition that the block releases has failed, either do not schedule thedefer(keep it after the success check) or make the block idempotent so a redundant release is harmless.No control-flow escape from the block. A
deferblock may throw an exception, but it may notreturnfrom the enclosing sub,gotoa label outside itself, or runnext,last, orredofor a loop that encloses thedefer. Those constructs are fine inside thedeferwhen they act on loops contained entirely within the block’s own body:defer { foreach (1 .. 5) { last if $_ == 3; } # permitted } foreach (6 .. 10) { defer { last if $_ == 8; } # forbidden }
Exception during exception unwind. If the
deferblock is running because an exception is propagating and the block itself throws, the language does not specify the resulting exception — only that the caller will see one. Do not rely on which exception wins. Wrap risky cleanup inevalif you need both signals preserved.Exceptions propagate normally. An exception thrown by a
deferblock that is running for any other reason (normal exit, return, loop control) propagates to the caller exactly like any other exception. A failingclosein cleanup can therefore turn a successful-looking function into a caller-visibledie.$@and$!inside the block. On the exception-driven exit path,$@holds the in-flight exception while thedeferblock runs. Reading it is fine; replacing it changes what the caller sees after the unwind completes.Scope granularity.
deferbinds to the immediately enclosing block, not to the enclosing sub. Adeferinsideif (...) { defer { ... } }runs when theifblock exits, not when the sub returns. Put thedeferat subroutine scope if you want subroutine-exit semantics.Experimental warning. Under
use warningsthe first occurrence warns. Opt in deliberately withno warnings 'experimental::defer'once you have committed to the feature.Not available without the feature guard. Without
use feature 'defer'(or a bundle that enables it),defer { ... }is parsed as a call to a subroutine nameddeferfollowed by a block — almost certainly not what you meant, and usually a compile-time error.
Differences from upstream#
Fully compatible with upstream Perl 5.42. The feature remains marked experimental in pperl to mirror upstream’s classification; behaviour (scheduling on statement execution, LIFO order, exception propagation, restrictions on control-flow escape) matches Perl 5.42.
See also#
eval— the established tool for turning exceptions into return values;defercomplements it by guaranteeing cleanup without an explicitevalwrapper on every pathdie— one of the exit paths that triggers deferred blocks; read alongsidedeferwhen designing cleanupreturn— another exit path; adeferscheduled before areturnruns before the caller resumeslocal— the other idiom for scoped state restoration; picklocalfor dynamic scoping of a single variable,deferfor arbitrary cleanup actions$@— current exception; visible inside adeferblock running on the exception-unwind path$!— system errno; easily clobbered by cleanup syscalls,local-ise inside the deferred block if the caller needs it preserved