|
原文 提议添加新的__metadata存储类,来标记函数或聚集声明成员字段.
理由和动机
类型限定符传递性地应用于所有子类型:
struct A
{
int a;
int* b;
}
void main()
{
不变 A a;
}
但是有时,无论如何限定,特定字段要保持可变.如下示例: 如标准库中数组中RefCounted的用法:
struct RefCounted(T)
{
T data;
size_t count;
}
struct Array(T)
{
private struct Payload
{
T[] payload;
size_t capacity;
}
private RefCounted!Payload _data;
}
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;
}
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;
private __metadata const int* mc;
private __metadata immutable int* imc;
}
除了__metadata字段,不变和常仍是可传递的.修改__metadata字段,是已定义的行为.
struct S
{
int p;
private __metadata int m;
void increment() immutable
{
++m;
++p;
}
}
__metadata不应用传递性.
struct RefCount
{
immutable size_t id;
size_t count;
}
struct T
{
private __metadata RefCount s;
void updateRC() immutable
{
++s.count;
++s.id;
}
}
限制
全局/局部和静态变量不能是__metadata:
module jaja;
__metadata int x = 2;
int fun()
{
__metadata int y;
}
struct S
{
private __metadata static int b;
}
只能在@system代码中操作__metadata数据:
struct S
{
private __metadata int* m;
}
int* bar() @safe
{
S s;
return s.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);
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参数的函数.
|