d的元数据
原文
提议添加新的__metadata
存储类,来标记函数
或聚集
声明成员字段
.
理由和动机
类型限定符
传递性地应用于所有子类型
:
struct A
{
int a;
int* b;
}
void main()
{
不变 A a;//aa和ab都是不变
}
但是有时
,无论如何限定
,特定字段
要保持可变
.如下示例:
如标准库
中数组
中RefCounted
的用法:
struct RefCounted(T)
{
T data;
size_t count;
/* RefCounted方法*/
}
struct Array(T)
{
private struct Payload
{
T[] payload;
size_t capacity;
}
private RefCounted!Payload _data;
/* Array方法*/
}
void main()
{
不变 Array!(int*) arr;
}
上例,声明不变
数组.根据当前语言规则
,引用计数
字段的_data
成员是不变
,使得RefCounted
的count
成员,也是不变
.导致无法按全封装
方式计算不变对象
引用.
std.datetime
中Rebindable
用法:
struct Rebindable(T)
{
import std.traits : Unqual;
private union
{
T original;
Unqual!T stripped;
}
//其他有趣的方法
}
class TimeZone {}
struct SysTime
{
Rebindable!(不变 TimeZone) _timezoneStorage;
}
void main()
{
不变 SysTime a;
}
按不变
声明SysTime
实例时,SysTime
中可重绑定
对象也使不变
无用:可重绑定
对象的全部意义
在于绑定
到其他
.
一般按对象
一部分声明
分配器.如果上述对象
是不变
,则分配器
无法修改其内部数据
.
以上,限定符
传递性使得很难
编写干净,封装
代码.为了缓解
这些问题,需要打破限定符传递性
的方法.因而引入__metadata
:可在成员
字段的声明
中使用,从而打断传递性
.如:
struct RefCounted(T)
{
T data;
size_t count;
/* RefCounted方法*/
}
struct Array(T)
{
private struct Payload
{
T[] payload;
size_t capacity;
}
private __metadata RefCounted!Payload _data;
/* 数组方法 */
}
void main()
{
不变 Array!(int*) arr;
}
现在按__metadata
标记RefCounted
对象,即使对象自身是不变
,Array
和RefCounted
内部都可修改
他们,这样达到引用计数
目的.
语义
__metadata
存储类修改字段
上类型限定符
的传播性.__metadata
只可应用于private
,可变
成员.
struct S
{
int* p;
shared int* s;
private __metadata
{
int* m;
shared int* ms;
}
__metadata int* pm;
//错误:`__metadata`字段必须是`private`
private __metadata const int* mc;
//错误:不能同时是__metadata和const
private __metadata immutable int* imc;
//错误:不能既是__metadata又是不变的
}
除了__metadata
字段,不变
和常
仍是可传递
的.修改__metadata
字段,是已定义
的行为.
struct S
{
int p;
private __metadata int m;
void increment() immutable
{
++m;// 好,m是元数据
++p;//错误,无法修改S不变实例的p字段
}
}
__metadata
不应用传递性
.
struct RefCount
{
immutable size_t id;
size_t count;
}
struct T
{
private __metadata RefCount s;
void updateRC() immutable
{
++s.count; // 好
++s.id;// 错误:无法修改`id`不变字段
}
}
限制
全局/局部和静态变量
不能是__metadata
:
module jaja;
__metadata int x = 2;
//错误:全局变量不能是`__metadata`
int fun()
{
__metadata int y;
错误:局部变量不能是 `__metadata`
}
struct S
{
private __metadata static int b;
//错误:静态变量不能是 `__metadata`
}
只能在@system
代码中操作__metadata
数据:
struct S
{
private __metadata int* m;
}
int* bar() @safe
{
S s;
return s.m;
//错误,在`bar`@安全函数中无法访问`m`元数据.
//必须是@系统
}
shared
因为不变
是隐式共享
的,为了线程安全
,按shared
对待不变
实例的__metadata
字段:
struct S
{
private __metadata int* p;
}
immutable S s;
static assert(is(typeof(s.p) == shared(int*)));
可从可变
,const
和不变
对象得到const
对象,因此类型系统
无法推导对象内__metadata
字段应为共享或不共享
.按非共享
可变类型检查
,const
对象的__metadata
字段,让用户理解
代码并正确的强制转换
:
shared int* q;
int* r;
struct A
{
private __metadata int* p;
bool isImmutable;
this(shared int *p) immutable
{
this.p = p;
isImmutable = true;
}
void fun() const
{
pragma(msg, typeof(p).stringof);
// typeof(p) => int*
if (isImmutable)
q = cast(shared int*) p;
else
r = p;
}
}
void main()
{
shared int* p
immutable A ia = immutable A(p);
const A ca;
ia.fun();
ca.fun();
}
可从可变
,const
和不变
对象得到inout
对象
.__metadata
字段的inout
属性按常
一样类似
对待.
pure
添加__metadata
后,不变
实例,按函数参数传递时,不会提供相同
的纯度保证
:
struct S
{
private __metadata int* p;
void inc() immutable pure
{
*p += 1;
}
}
void foo(ref immutable S a) pure
{
a.inc();
}
int x;
void main()
{
A b = A(&x);
foo(b);
}
此DIP
之前,按强纯
函数对待foo
,但添加__metadata
后,现在可通过元数据
字段修改
实例,因此foo
成为弱纯
函数.该DIP
之后,仅当参数不包含元数据
字段时,才按强纯
函数对待
带不变ref/pointer
参数的函数.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现