State переменные в Perl

Tags:

В Perl существует особый тип переменных под названием state.

В доке про них написано:

state declares a lexically scoped variable, just like my. However, those variables will never be reinitialized ...

На первый взгляд это дает нам возможность очень просто реализовывать счетчики и иже с ними:

sub count {
  state $count = 0;
  $count++;
}

Однако, есть нюанс - фраза will never be reinitialized означает что переменная действительно никогда не будет переинициализирована пока существует родительский скрипт. И это дает нам вот такую замечательную граблю на которую можно ненароком наступить:

Объявляем пакет:

package MyTestState;

use strict;
use feature 'state';

sub new {
  bless {}, shift;
}

sub count {
  state $count = 0;
  $count++;
}

1;

И саму программу:

use lib 'lib';
use v5.18;
use MyTestState;

my $mystate =  MyTestState->new();

for (0..10) {
  my $counter = $mystate->count();
  say "Counter [$counter]";
}

Вывод ожидаем:

Counter [0]
Counter [1]
Counter [2]
...
Counter [10]

А теперь добавляем такой код:

undef $mystate;

say "next object!=======";

my $mystate1 = MyTestState->new();
for (0..10) {
  my $counter = $mystate1->count();
  say "Counter [$counter]";

}

Здесь мы удаляем старый объект со счетчиком и создаем новый. Логично предположить что счетчик пойдет заново, но на самом деле нет. Не смотря на то что мы удалили старый объект и создали новый, переменная со счетчиком никуда не делась и не была переиницализирована! И при запуске программы мы увидим:

Counter [0]
Counter [1]
Counter [2]
...
Counter [10]
next object!
Counter [11]
Counter [12]
Counter [13]
...
Counter [20]
Counter [21]

Так что слово newer в документации действительно значит "никогда пока жив инстанс интерпретатора запустивший скрипт".