The basic reason is that local doesn't actually alter the contents of a variable. It allocates a new one and makes sure that the access path to the original variable now temporarily leads to the new variable. This means however that there has to be a primary access path to the variable, or the trick won't work. You also have to be sure that the original location will not disappear before the restore.

For package vars, this primary access path is the glob. Each package contains a glob for each name that occurs in it. This glob can contain a scalar variable, a hash variable, an array variable, an IO handle etc.

When you do local $Foo; (where $Foo is a package var) perl will save the original scalar variable (not its contents) on stack, create a new scalar variable, and insert it into the glob's SCALAR field. It will also increment the refcount on the glob so it will surely not be deallocated.

•(Update: a local doesn't get initialized with the original value... I thought it was..)

On return, the localized variable is discarded, the original variable is placed back in the glob, and the glob's refcount is decremented. Everything is back to normal.

Note that you can easily see this behavior: just take a reference to $Foo before and after localizing it. You'll see they reference different and independent variables. (Keeping a reference to the localized var will ofcourse keep it alive, even if the local has already been restored

The procedure is identical for local %Foo and local @Foo, except they operate on the HASH and ARRAY fields of the glob. When you do local *Foo, it does essentially the same procedure but for all fields of the glob at once. They're put together in a structure to which the glob keeps a pointer, so it doesn't need to save each var separately. Note that it can't just allocate a new glob and place it into the package, since all existing code would still use the old glob, which they reference directly.

Now for lexical variables... they have no glob! :-)
That doesn't mean it's truly impossible to localize them (I haven't examined how my-vars are implemented exactly), but it surely means that the mechanism described above can not be reused for them.

As I mentioned in my original note, localizing array and hash elements works completely different. Here you specify an array or hash in any way you like, and the index or key of an element. local $foo[3] then takes the scalar variable (again, not its contents) which is the element at that index (or key), saves it on the stack, along with a reference to the array/hash (which also means the array/hash will surely not get destroyed) and the index/key of the element. It creates a new scalar variable and places in the original location. When the block ends, it places the saves element back, discarding the variable that was there.

Again, you can check this by taking a reference. You'll see that the localized $foo[3] is a different and independent variable than the original.

Ok, that's it I think... any questions? :-)

•Update: Here's some example code with output:

$\ = "\n"; # automatically add newlines to output $Foo = 4; # create the var print '*Foo{SCALAR} = ', *Foo{SCALAR}; # print the glob entry my $x = \$Foo; # make a reference... which is print '$x = ', $x; # the same as the glob entry.. duh $$x = 5; # change via reference print '$Foo = ', $Foo; # var has changed my $y; print "Enter scope"; { local $Foo = 42; # let the magic begin! print '*Foo{SCALAR} = ', *Foo{SCALAR}; # it's different! $y = \$Foo; # take a ref to this new thing print '$y = ', $y; # the same as the *new* glob entry print '$Foo = ', $Foo; # foo is 42 print '$$x = ', $$x; # print old var via reference.. unchanged print '$$y = ', $$y; # print new var via reference.. it's 42 $$x = 6; # change via reference to *old* print '$Foo = ', $Foo; # nothing happened to the local one! # but wait... } print "Exit scope"; print '*Foo{SCALAR} = ', *Foo{SCALAR}; # old glob entry is back print '$Foo = ', $Foo; # and we changed this one via $x earlier! print '$y = ', $y; # now the last remaining reference to local va +r print '$$y = ', $$y; # still 42
Gives this output (I added comments):
*Foo{SCALAR} = SCALAR(0x511f4) # original entry in glob $x = SCALAR(0x511f4) # ref to original scalar (same) $Foo = 5 # original value Enter scope *Foo{SCALAR} = SCALAR(0x47650) # new SCALAR entry in glob $y = SCALAR(0x47650) # ref to it $Foo = 42 # value of localized var $$x = 5 # value of original $$y = 42 # value via ref to localized var $Foo = 42 # after editing original, local unchan +ged Exit scope *Foo{SCALAR} = SCALAR(0x511f4) # original entry back in glob $Foo = 6 # change we made via $$x now visible $y = SCALAR(0x47650) # the localized var still around via r +ef $$y = 42 # still contains value

In reply to Local: Behind the Scenes by xmath
in thread local element of my() array???? by Anonymous Monk

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.