You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Perl Moose属性的完全不可变?

在Moose中实现内部不可变的引用属性

问题描述

Moose里的ro属性仅能保护引用本身不被重新赋值,却无法阻止修改引用的内部结构。比如下面的代码中,push操作可以直接修改list属性的数组内容:

package SomeClass;
use Moose;

has 'list' => (
    is => 'ro',
    isa => 'ArrayRef',
);

package main;

use warnings; use strict;
use v5.36;
use feature qw(try);
no warnings qw(experimental::try);
use Data::Printer;

my $x = SomeClass->new({
    list => [qw(a b c)]
});

# 希望禁止这个操作
push $x->list()->@*, 'd';

p $x;

# 输出结果:list被修改
#list   [
#            [0] "a",
#            [1] "b",
#            [2] "c",
#            [3] "d"
#        ]

我们期望实现类似Const::Fast的效果——修改数组内部时抛出错误:

use Const::Fast;
const my $LIST => [qw(x y z)];

try {
    push $LIST->@*, 'no way';
} catch ($e) {
    warn $e;
}

# 报错:Modification of a read-only value attempted at ... line ...
# 输出的LIST保持只读状态

解决方案

方法1:通过coerce生成只读引用

使用coerce特性,将传入的数组引用复制一份并转为只读。这种方式既保护了类属性的内部结构,也不会影响外部原数组:

package SomeClass;
use Moose;
use Const::Fast;

has 'list' => (
    is => 'ro',
    isa => 'ArrayRef',
    coerce => 1,
    builder => sub { [] }, # 可选,提供默认空数组
    coerce => sub {
        my $original_arr = shift;
        # 复制原数组,生成新的只读引用
        const my $readonly_arr => [@$original_arr];
        return $readonly_arr;
    },
);

package main;

use warnings; use strict;
use v5.36;
use feature qw(try);
no warnings qw(experimental::try);
use Data::Printer;

my $x = SomeClass->new({ list => [qw(a b c)] });

try {
    push $x->list()->@*, 'd';
} catch ($e) {
    warn $e; # 抛出预期错误
}

p $x; # list保持初始的[a,b,c],且每个元素和数组本身都是只读的

方法2:通过trigger标记传入的引用为只读

如果希望直接将传入的引用转为只读(注意:这会修改原引用的可写性,外部原数组也会变成只读),可以使用trigger:

package SomeClass;
use Moose;
use Const::Fast;

has 'list' => (
    is => 'ro',
    isa => 'ArrayRef',
    trigger => sub {
        my ($self, $arr) = @_;
        const $arr; # 直接标记传入的数组引用为只读
    },
);

效果验证

运行修改后的代码,尝试修改list内部元素或数组结构时,会抛出Modification of a read-only value attempted的错误,数组内容保持不变,和Const::Fast的行为一致。

内容的提问来源于stack exchange,提问作者Jan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 14:17:02