perl eval Use of uninitialized value declared in package

615 views Asked by At

can anyone explain to me why a variable that is declared in package can not be accessed by eval function unless it is used one time inside a sub?
(perl v5.16.3 MSWin32-x64-multi-thread ActiveState)

Package:

use strict;
use warnings;
package testPackage;
my $insertVar = "TEST";
sub testSub {
    my $class = shift;
    my $test = shift;
    eval '$test="'.$test.'";';
    return $test;
}
1;

Program:

use strict ;
use warnings ;
use testPackage ;
my $testVar = q[insertVar = ${insertVar}] ;
$testVar = testPackage->testSub( $testVar ) ;
print "$testVar\n" ;

Result when executing program:

Use of uninitialized value $insertVar in concatenation (.) or string at (eval 1) line 1. insertVar =

Now if I use the variable inside testSub (by e.g. printing it):

use strict;
use warnings;
package testPackage;
my $insertVar = "TEST";
sub testSub {
    my $class = shift;
    my $test = shift;
    print $insertVar . "\n";
    eval '$test="'.$test.'";';
    return $test;
}
1;

Then the program runs exactly as I intended:

TEST

insertVar = TEST

1

There are 1 answers

3
ikegami On BEST ANSWER

my variables declared in a file (outside of curlies) go out of scope when the file finishes executing (before require and use return).

$insertVar will only continue to exist after the file has finished executing it if it's captured by a sub, and it will only be visible to the subs that capture it.

For efficiency reasons, subs capture the fewest variables possible. If the sub doesn't reference $insertVar, it won't capture it. Since your first testSub doesn't reference $insertVar, it doesn't capture it. Since $insertVar has gone out of scope by the time you call testSub, it's not available to eval. You should get the warning Variable "$insertVar" is not available, but for reasons unknown to me, it's issued for the specific code you used.

Your second testSub reference $insertVar, so testSub captures $insertVar and keeps it alive. Even though $insertVar has gone out of scope by the time you call testSub, it's available to eval since it was captured by the sub.

If you declare the variables using our, they'll be global package variables, and thus won't go out of scope, and they'll be available to eval.

>perl -wE"use strict; use warnings; { my $x = 'abc'; sub foo { $x } } say foo()"
abc

>perl -wE"use strict; use warnings; { my $x = 'abc'; sub foo { eval '$x' } } say foo()"
Variable "$x" is not available at (eval 1) line 2.
Use of uninitialized value in say at -e line 1.


>perl -wE"use strict; use warnings; { our $x = 'abc'; sub foo { eval '$x' } } say foo()"
abc