В Perl, почему копирование слабой ссылки создает нормальную, сильную ссылку?

Scalar::Util::weaken говорит:

ПРИМЕЧАНИЕ. Копирование слабой ссылки создает нормальную, сильную ссылку.

Я не могу понять, почему Perl обрабатывает его таким образом. В моих приложениях я использую weaken для прерывания циклов. Иногда мне приходится ослабить ссылки, которые будут уже слабыми, если Perl не будет действовать таким образом.

Ответ 1

Я думаю, что это проблема инкапсуляции. Если сторонняя библиотека использует слабые ссылки внутри, мой код не должен заранее знать, что когда я делаю копию справки, которая может внезапно исчезнуть у меня. Обычное ожидание в Perl состоит в том, что ref останется действительным до тех пор, пока он существует. Когда вы вызываете weaken, вы в основном пообещали, что предпримите необходимые шаги, чтобы проверить, что ссылка все еще действительна до ее использования.

В качестве второй причины интерфейс, чтобы ослабить сильную копию слабого рефлекса, довольно прост.

my $new_ref = $old_ref; if (isweak($old_ref)) { weaken($new_ref); }

Код, чтобы сделать то же самое, чтобы получить сильный ref, если слабый ref создал слабый ref немного сложнее.

my $new_ref;
if (ref($old_ref) eq 'ARRAY') {
    $new_ref = \@{$old_ref};
}
elsif (ref($old_ref) eq 'HASH') {
    $new_ref = \%{$old_ref};
}
elsif (.....

Если вы знаете, что ref может быть только одним типом, вы можете сохранить каскад if/elsif и просто сделать deref-reref, но еще сложнее судить о том, почему вы разыменовали только новую ссылку. Следующий сопровождающий попытается "исправить" ваш код.

Ответ 2

При копировании ссылки в новую переменную счетчик ссылок увеличивается. Это верно при копировании слабой или сильной ссылки.

my $obj = {};    # 1 reference to {} stored in $obj

my $copy = $obj; # 2 references

weaken $obj;     # 1 reference

в этот момент, если $copy выходит за пределы области видимости, счетчик ссылок упадет до нуля, и память будет освобождена. Теперь предположим следующий код:

my $newref = $obj;  # 2 references

undef $copy;        # 1 reference

Если Perl сохранил слабую ссылку в $newref, тогда хэш будет неожиданно удален, когда $copy будет очищен. Это сломало бы ожидание того, что при копировании ссылки он будет по крайней мере придерживаться до тех пор, пока копия.

Короче говоря, если слабые ссылки сохранялись по назначению, это заставило бы вас засорять ваш код с помощью бесчисленных проверок слабости и потребовать другой способ разблокировать переменную, чтобы избежать неизбежных переменных проблем самоубийства.

Ответ 3

Я не уверен, почему это поведение по умолчанию, но вот решение, основанное на коде из Scalar:: Util:

$ref  = \$foo;
$weak = isweak($ref);               # false
weaken($ref);
$weak = isweak($ref);               # true

# copying a weak reference creates a new strong one
$copy = $ref;
$weak = isweak($copy);              # false

# the solution is simply to weaken the copy
$weaken($copy);
$weak = isweak($copy);              # true

Если вы хотите сделать подпрограмму, которая использовала слабую ссылку в качестве аргумента и вернула ослабленную копию этой ссылки, это было бы легко, используя код, показанный выше.