How can I automatically release RAII style resources in Perl?

Let's say I have a resource (for example, a file descriptor or a network socket) that needs to be freed:

open my $fh, "<", "filename" or die "Couldn't open filename: $!"; process($fh); close $fh or die "Couldn't close filename: $!"; 

Suppose process can die. Then the code block exits early and $fh does not close.

I can explicitly check for errors:

 open my $fh, "<", "filename" or die "Couldn't open filename: $!"; eval {process($fh)}; my $saved_error = $@ ; close $fh or die "Couldn't close filename: $!"; die $saved_error if $saved_error; 

but this type of code, as you know, is difficult to get correctly, and only gets more complicated when adding additional resources.

In C ++, I used RAII to create an object that owns the resource and whose destructor frees it. This way, I don’t have to forget to free the resource, and the cleaning of the resources happens correctly as soon as the RAII object goes out of scope - even if an exception is thrown. Unfortunately, in Perl, the a DESTROY method is unsuitable for this purpose, since there is no guarantee when it will be called.

Is there a Perlish method that automatically releases resources, even with exceptions? Or an obvious error checking the only option?

+4
source share
3 answers

I think Scope :: Guard was designed to help with.

 #!/usr/bin/perl use strict; use warnings; use Scope::Guard; my $filename = 'file.test'; open my $fh, '>', $filename or die "Couldn't open '$filename': $!"; { my $sg = Scope::Guard->new( sub { close $fh or die "Could not close"; warn "file closed properly\n"; } ); process($fh); } sub process { die "cannot process\n" } 

However, as @Philip notes in the comments, Scope::Guard uses the DESTROY method, which creates some uncertainty as to when the area exit code will be run. Modules like Hook::Scope and Sub::ScopeFinalizer look great, although I never used them.

I like Try :: Tiny for its clean interface and simplicity, and it will help you handle exceptions correctly:

 #!/usr/bin/perl use strict; use warnings; use Try::Tiny; my $filename = 'file.test'; open my $fh, '>', $filename or die "Couldn't open '$filename': $!"; try { process($fh); } catch { warn $_; } finally { close $fh and warn "file closed properly\n"; }; sub process { die "cannot process\n" } 
+4
source

My Scope :: OnExit module is just for that.

+4
source

The good thing about lexical file descriptors is that they will be closed (and freed) when they go out of scope. So you can just do something like this:

 { # bare block creates new scope open my $fh, "<", "filename" or die "Couldn't open filename: $!"; eval { process($fh) }; # handle exceptions here close $fh or die "Couldn't close filename: $!"; } # $fh is now out of scope and goes away automagically. 
+3
source

Source: https://habr.com/ru/post/1304906/


All Articles