Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
376 views
in Technique[技术] by (71.8m points)

perl - Why does $foo->{bar} autovivify but %$foo doesn't?

I have the following code:

$headers;
some_sub( %$headers );

When I call some_sub I get an error:

Can't use an undefined value as a HASH reference at ...

But similar code does not produce an error:

$headers->{ x };

Why doesn't autovivification work the same way in the first example as it does in the second?

UPD

I noted by @ThisSuitIsBlackNot. I really ask:

why my $h; $h->{foo} works and my $h; %$h doesn't

UPD
The real code:

my $email =  Email::Simple->create(
    header =>  [
        To             =>  $address,
        From           =>  $cnf->{ from },
        Subject        =>  $subject,
        'Content-Type' =>  'text/html; charset="utf8"',
        %$headers,
    ],
    body => $body
);
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Note   Code added to the question demostrates why autovivification doesn't happen.

Short   Your sub takes a list (hash) which has an anonymous array as an element – and %$headers is buried in that array. It is the anon array that is the scalar aliased to, thus there is no requirement for %$headers to be modifiable. Thus no autovivification happens, and you get the fatal runtime error described below, as dereferencing is attempted on an undefined reference.


A %$ref autovivifies when used in lvalue context. This may happen in a sub call, see below.

The error you show is due to the use of an undefined reference. For example, the statement

my %hash = %{ $ref };

attempts to copy a hash from the memory location stored in $ref and assign it to %hash. The symbol %hash is created at compile time, but if no hash is found at $ref or if there is nothing in $ref, we get an error. No autovivification happens here. With use strict in effect

perl -wE'use strict; my $h; my %h = %$h; say $h'

this throws the fatal runtime error

Can't use an undefined value as a HASH reference at -e line 1.

When eval-ed to survive that

perl -wE'use strict; my $h; my %h = eval { %$h }; say $h; say "hi"'

it prints a warning about "uninitialized value", an empty line, and then hi. No hash.

However, when used as an argument in a subroutine call it autovivifies

perl -wE'use strict; sub tt { 1 }; my $h; tt( %$h ); say $h'

as this prints the line HASH(0x257cd48), without warnings or errors.

The autovivification happens when a dereferenced object is used in lvalue context, which means that it needs to be modifiable. In a subroutine call the reason for this is that arguments to a function are aliased in @_ so it must be possible to modify them. The same aliasing need makes it happen in a foreach loop, while keys resets the hash iterator. See this post and this post and this post.

Thanks to ThisSuitIsBlackNot for explanation and links.

In your case the %$ref is passed as an element of an anonymous array, and is thus not aliased (the arrayref itself is). So autovivication does not kick in and you get that error.


On autovivification from perlglossary

In Perl, storage locations (lvalues) spontaneously generate themselves as needed, including the creation of any hard reference values to point to the next level of storage. The assignment $a[5][5][5][5][5] = "quintet" potentially creates five scalar storage locations, plus four references (in the first four scalar locations) pointing to four new anonymous arrays (to hold the last four scalar locations). But the point of autovivification is that you don’t have to worry about it.

Also see, for example, an article from Effective Perler


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...