You tried, directly or indirectly, to change the value of a
constant. This type of error frequently occurs at a distance and is difficult to trace. The magic of $_ is often involved, but is not responsible.
Common causes:
- Treating a loop variable as an lvalue
- Modifying $_ inside foreach, map or grep
- Modifying elements of @_ directly
- Modifying $a or $b inside sort
- Autovivifying $a or $b inside sort
- Modifying an unlocalized $_
Treating a loop variable as an lvalue
In this example $x is aliased to the constant '1', so when the loop body attempts to increment $x an error is triggered. See Lists With Constant Values for more details.for my $x (1,2) { $x++; }
Modifying $_ inside foreach, map or grep
In all of these examples $_ is aliased to a constant, and when the loop body attempts to modify $_ an error is triggered. See Lists With Constant Values for more details.for (1,2) { chomp; } for ("foo", @list) { s/foo/bar/; } @array = map { $_++ } (1,2); @array = grep { $_++ } (1,2);
Modifying elements of @_ directly
Modifying elements of @_ directly allows you to modify the variable that was passed to the function. For example, in the above example $n is now 2. But an error will occur when a constant is passed, as in the second call.sub incr { $_[0]++; } my $n = 1; incr($n); # good incr(1); # bad
Modifying $a or $b inside sort
It is permissible (but ill-advised) to modify $a and $b within sort. However, modifying a constant that is aliased to $a or $b is still an error.@array = sort { $a++ } (1,2);
Autovivifying $a or $b inside sort
The variables $a and $b are aliased to each item in the list being sorted, and as such modifying them is possible - but will cause an error if the current element is unmodifiable. A common cause of this is sorting an array of references when where the list has a gap. In this situation $a will be undef, and autovivification by dereferencing will trigger an error.my @bad; $bad[0] = [1]; $bad[2] = [2]; @bad = sort {$a->[0] <=> $b->[0]} @bad;
Modifying an unlocalized $_
This example will cause an error because the for loop aliases $_ to the literal '1', and then calls prompt_user which attempts to read a line from STDIN and store it in $_ - which is still aliased to '1'.for (1,2) { my $data = prompt_user(); } sub prompt_user { print "Enter a number\n"; while (<STDIN>) { # Do stuff } }
The error will also occur in this simplified scenario:
for (1,2) { while (<STDIN>) { } }
Guidelines to avoid read-only errors:
- Don't treat loop variables as a lvalue if there is any chance a constant value will be included
- Don't modify an unlocalized $_
- Don't modify $_ inside map or grep
- Don't modify $a or $b inside sort
- Don't dereference $a or $b inside sort without checking that they exist and are references
- Don't use $_ for the loop variable unless it is a very trivial loop
- Don't modify elements of @_ directly
Notes
Lists With Constant Values
Within the context of this document the important thing to understand is what expressions result in modifiable objects. The following expressions have constants in them:And the following are safe:$_++ for (1,2); $_++ for (1,@array); @array = map {$_++} (1,@array);
my @array = (1,2); for (@array) { $_++; }
For an explanation of lists versus arrays I recommend the following:my ($x,$y) = (1,2); for ($x,$y) { $_++; }
|
---|
Replies are listed 'Best First'. | |
---|---|
Re: Common Causes for "Modification of a read-only value attempted"
by techcode (Hermit) on Jul 15, 2009 at 10:59 UTC | |
Re: Common Causes for "Modification of a read-only value attempted"
by maxx27 (Novice) on Jul 10, 2016 at 15:12 UTC |
Back to
Tutorials