Modification of a read-only value attempted

The most common cause for this error is that perl attempted to write to a variable that is currently aliased to a constant.
The magic of $_ is often involved.

Possible causes:

Using magical $_ behaviour in non-trivial cases

The magical properties of $_ are great for saving a few keystrokes, but should be limited to use in small, strictly local blocks.

In this example the for loop is iterating over the literal values '1' and '2', aliasing $_ to them.

When the inner while loop reads a line from STDIN, it assigns the value to $_. This triggers the read-only error, because at this time $_ is the literal 1.

for (1,2) { while (<STDIN>) { } }
This error frequently occurs at a distance because a subroutine called within the loop modifies an unlocalized $_.

Treating a list as an array

The difference between a list and an array is often ignored, which can cause problems when modifying $_.

The intent in the following example is to iterate over two string literals, chomp the newline, and output the text:

for ("foo\n", "bar\n") { chomp; print; }
This causes the read-only error to occur, because within the context of that loop $_ is aliased to the string literal "foo\n".

Note that the problem is with the modification of the string literal. It is not directly related to $_. The error occurs with this as well:

for my $str ("foo\n", "bar\n") { chomp $str; print $str; }
map and grep are common sources for this problem as well.
@bad = map {$_++} (1,2); @bad = grep {$_++} (1,2);

Modifying @_ elements inside a subroutine

In this example the bad() subroutine attempts to modify the first parameter directly, using $_[0].
This causes problems if a literal value is passed.
sub bad { $_[0]++; } bad(1);

Modifying $a or $b inside sort

It is permissible (but ill-advised) to modify $a and $b within sort. (Tested for perl 5.6.2, 5.8.2, and 5.8.4)
my @good = (1,2); @good = sort {$a++ <=> $b} @good;
However, modifying a literal that is assigned to $a or $b is still an error.
@bad = sort {$a++ <=> $b} (1,2);

Dereferencing $a or $b inside a sort block without testing that they are references

Very closely related to the previous problem is the issue of dereferencing $a or $b when you do not know that they exist and are a reference. If $a is undefined and you attempt to use it as an array reference, perl will attempt to autovivify $a as an arrayref, which modifies the readonly value. Splode.
my @bad; $bad[0] = [1]; $bad[2] = [2]; @bad = sort {$a->[0] <=> $b->[0]} @bad;

Guidelines to avoid this error:

  1. Don't modify an unlocalized $_
  2. Don't modify $_ inside of map
  3. Don't modify $a or $b inside sort
  4. Don't dereference $a or $b inside sort without checking that they are references
  5. Don't use $_ for the loop variable unless it is a very trivial loop
  6. Don't modify elements of @_ directly

Replies are listed 'Best First'.
Re: RFC - FAQ for Modification of a read-only value attempted
by chromatic (Archbishop) on Aug 29, 2006 at 08:59 UTC
    The difference between a list and an array is often ignored...

    Indeed; for never iterates over an array. for always iterates over a list.

      The difference between a list and an array is often ignored...
      Indeed; for never iterates over an array. for always iterates over a list.

      What do either of those mean? Based on later replies, it seems like people are interpretting it in terms of whether or not $_ is an alias or a copy. But saying "$_ is an alias" or "$_ is a copy" actually means something clear.

      Is there some other meaning? For example, when foreach is given an array, it might make a new list of aliases to each array member and then iterate over that list, and thus not ever directly iterating through the array. Now that I think of that, it fits what you wrote quite well, but doesn't match how others interpretted it. I guess this behavior matters if you modify the array (adding or removing members) while you are in the loop and I suspect that is what foreach does.

      Is that all that you meant or are there other aspects?

      imp, what did you mean?

      - tye        

        In the first draft of this document I discussed list versus array, but decided to trim it down before posting since it was outside of my intended scope.

        In that version I referenced a post by you that provided what I thought was a nice explanation:
        Re: Differ. array and List (there is no List)

      Indeed; for never iterates over an array. for always iterates over a list.

      Doesn't this suggest that for iterates over the array, rather than over a list generated from the array?

      c:\test>perl -wle"@a = 'a'..'f'; for( @a) { ++$_ }; print qq[@a]" b c d e f g

      Maybe this is semantics, but I'd like to understand.


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
        No.
        C:\>perl -wle"my $bar = 'NN';my @foo = 'b'..'c';for ( $bar, @foo ){ ++ +$_ } print qq! $bar @foo!" NO c d C:->
      After further thought, and a little caffeine, I have to confess that I'm not sure I understand the difference in this case. Doesn't the following example show that for does iterate over the array elements?
      my @array= (1,2,3,4); for my $item (@array) { pop @array; printf "Item: %s Array: %s\n",$item,join(', ', @array); }
      Output:
      Item: 1 Array: 1, 2, 3 Item: 2 Array: 1, 2
      If for was iterating over a list that would imply that it wa s divorced from the @array object, and that modifying the @array object would not affect the loop - no?

        It does modify the elements because the elements are not copied to the list, instead, they are referenced there:

        use Devel::Peek; my @array = (1,2,3); Dump \@array; print "====\n"; foreach (@array) { Dump $_; print "----\n"; }

        Will output:

        SV = RV(0x193de54) at 0x22593c REFCNT = 1 FLAGS = (TEMP,ROK) RV = 0x226680 SV = PVAV(0x22a95c) at 0x226680 REFCNT = 2 FLAGS = (PADBUSY,PADMY) IV = 0 NV = 0 ARRAY = 0x19240d4 FILL = 2 MAX = 3 ARYLEN = 0x0 FLAGS = (REAL) Elt No. 0 SV = IV(0x1926b90) at 0x225858 REFCNT = 1 FLAGS = (IOK,pIOK) IV = 1 Elt No. 1 SV = IV(0x1926b94) at 0x225918 REFCNT = 1 FLAGS = (IOK,pIOK) IV = 2 Elt No. 2 SV = IV(0x1926b98) at 0x225930 REFCNT = 1 FLAGS = (IOK,pIOK) IV = 3 ==== SV = IV(0x1926b90) at 0x225858 REFCNT = 2 FLAGS = (IOK,pIOK) IV = 1 ---- SV = IV(0x1926b94) at 0x225918 REFCNT = 2 FLAGS = (IOK,pIOK) IV = 2 ---- SV = IV(0x1926b98) at 0x225930 REFCNT = 2 FLAGS = (IOK,pIOK) IV = 3 ----

        As you can see, the IVs printed in the for loop are the same ones from the array (same pointer value) but they now have a reference count REFCNT=2

        This means that the IVs were referenced in the new list, so that's why modifying them there will also modify them in the array.

        --
        Leviathan.
        No. Example Re^3: RFC - FAQ for Modification of a read-only value attempted Also see perlsyn (RTFM is good for you):
        If any element of LIST is an lvalue, you can modify it by modifying VAR inside the loop. Conversely, if any element of LIST is NOT an lvalue, any attempt to modify that element will fail. In other words, the "foreach" loop index variable is an implicit alias for each item in the list that you're looping over.
      Good point. I need to clarify that and iterate over a list of values in an array, versus the list of literals. Thanks.

      The builtins for, map, grep, sort all operate on lists of references; same with foreach.

      If for weren't iterating over a list of references, constructs like for(sort keys %hash) would not work as expected (or as they do ;-)

      Consider this thospel code snippet

      perl -le '@a=qw(a e f c b d); $_=$i++ for sort @a; print @a' 045213

      which prints out the sequence of array indices needed to access the unsorted array elements in sorted order.

      --shmem

      _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                    /\_¯/(q    /
      ----------------------------  \__(m.====·.(_("always off the crowd"))."·
      ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      post 300
RFC - FAQ for Modification of a read-only value attempted - Second Draft
by imp (Priest) on Aug 30, 2006 at 04:28 UTC

    Modification of a read-only value attempted

    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:

    1. Treating a loop variable as an lvalue
    2. Modifying $_ inside foreach, map or grep
    3. Modifying elements of @_ directly
    4. Modifying $a or $b inside sort
    5. Autovivifying $a or $b inside sort
    6. Modifying an unlocalized $_

    Treating a loop variable as an lvalue

    for my $x (1,2) { $x++; }
    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.

    Modifying $_ inside foreach, map or grep

    for (1,2) { chomp; } for ("foo", @list) { s/foo/bar/; } @array = map { $_++ } (1,2); @array = grep { $_++ } (1,2);
    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.

    Modifying elements of @_ directly

    sub incr { $_[0]++; } my $n = 1; incr($n); # good incr(1); # bad
    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.

    Modifying $a or $b inside sort

    @array = sort { $a++ } (1,2);
    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.

    Autovivifying $a or $b inside sort

    my @bad; $bad[0] = [1]; $bad[2] = [2]; @bad = sort {$a->[0] <=> $b->[0]} @bad;
    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.

    Modifying an unlocalized $_

    for (1,2) { my $data = prompt_user(); } sub prompt_user { print "Enter a number\n"; while (<STDIN>) { # Do stuff } }
    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'.

    The error will also occur in this simplified scenario:

    for (1,2) { while (<STDIN>) { } }

    Guidelines to avoid read-only errors:

    1. Don't treat loop variables as a lvalue if there is any chance a constant value will be included
    2. Don't modify an unlocalized $_
    3. Don't modify $_ inside map or grep
    4. Don't modify $a or $b inside sort
    5. Don't dereference $a or $b inside sort without checking that they exist and are references
    6. Don't use $_ for the loop variable unless it is a very trivial loop
    7. 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:
    $_++ for (1,2); $_++ for (1,@array); @array = map {$_++} (1,@array);
    And the following are safe:
    my @array = (1,2); for (@array) { $_++; }
    my ($x,$y) = (1,2); for ($x,$y) { $_++; }
    For an explanation of lists versus arrays I recommend the following:
Re: RFC - FAQ for Modification of a read-only value attempted
by diotalevi (Canon) on Aug 29, 2006 at 17:00 UTC

    You spend far too much time mentioning $_ when you and I both know it's irrelevant. Whether my alias is accessed via $_ or a named variable, it's the aliasing that's screwing up the person that's running into this message.

    ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

      Good point - I let my original intent of covering the cases that appear most frequently in SoPW take control of the document.

      I'll rework the examples to focus on the common problem areas more directly, and add a separate section for cautions related to $_.

      Thanks for the feedback.

Re: RFC - FAQ for Modification of a read-only value attempted
by husker (Chaplain) on Aug 30, 2006 at 15:22 UTC
    Very nice. I just might suggest that along with all the pieces of "broken" code you provide, that you show a "fixed" version as well. That will multiply the learning opportunities for whomever is running into this kind of error.