JohnnyLoo
JohnnyLoo

Reputation: 927

State variable persists even after object has been destroyed

I have a "state" variable in a module method to cache the first value generated by the method. The issue is that the method keeps using the state variable value even after the object has been destroyed and a brand new object has been created and uses the method.

This really looks like a bug in the perl state variable implementation, but I'm not sure. I know I could create a module attribute to cache the value, but I'd like to do it at the method level. Here is an example:

package MyTest;
use 5.010001;
use strict;

sub new {
    my $class = shift;
    return bless {}, $class;
}

sub generate_id {
    my $self = shift;
    state $generated_id;
    
    return $generated_id
        if defined $generated_id;
    
    $generated_id = "XYZ-" . int(rand 10000);
    return $generated_id;
}

sub DESTROY {
    say "Object has been destroyed";
}

1;

Here is a script calling it. As you will see, the value generated the first time by the "generate_id" method will keep coming back even after "$c" variable goes out of scope and a new one gets generated. Isn't this a bug? I thought all variable would be destroyed (including state variables) as well as method content after an object goes out of scope.

# Script.pl
use MyTest;

{
  my $id_gen = MyTest->new();
  say $id_gen->generate_id();
}

{
  my $id_gen = MyTest->new();
  say $id_gen->generate_id();
}

{
  my $id_gen = MyTest->new();
  say $id_gen->generate_id();
}

I get:

XYZ-6345
Object has been destroyed
XYZ-6345
Object has been destroyed
XYZ-6345
Object has been destroyed

Upvotes: 1

Views: 150

Answers (2)

tobyink
tobyink

Reputation: 13664

Yes, that's the point of state. The variable retains its state. The clue is in the name.

I thought all variable would be destroyed (including state variables) as well as method content after an object goes out of scope.

The variable would go out of scope when the method gets destroyed, yes, but the method doesn't get destroyed when the object gets destroyed. The method gets destroyed when the class gets destroyed. And classes don't really get destroyed until the program exits. (You may be able to do it with a lot of manual symbol table hacking, but I wouldn't recommend that.)

It sounds like what you want is this:

sub generate_id {
    my $self = shift;
    $self->{generated_id} ||= "XYZ-" . int(rand 10000);
}

Upvotes: 4

mscha
mscha

Reputation: 6840

Perl 5 doesn't have real methods, generate_id is just a sub, which is called with the object instance as its first parameter. So, the state variable $generated_id is cached for the entire run of the program – and will contain the same value for any instance that you call it with. You probably want to use an attribute for the generated ID.

Upvotes: 3

Related Questions