d安全引用计数
原文
要加个@trusted
包装器,才能同域
一起用.
析构器检查dip1000
,否则保持@system
.
import std.stdio;
import std.typecons;
struct Container
{
ubyte[] data;
}
struct Blob
{
ubyte[64] data;
}
void main ()
{
auto ptr = getPtr();
writeln(ptr);
const save = ptr.dup;
auto other = getPtr();
assert(save == ptr);
}
ubyte[] getPtr ()
{
Blob blob;
RefCounted!Container rc;
escape(rc.refCountedPayload(), blob);
assert(rc.refCountedPayload().data !is null);
return rc.refCountedPayload().data;
}
void escape (ref Container rc, ref Blob local)
{
rc.data = local.data;
}
编译,运行,崩溃.
不过,更像是编译器
错误而不是RefCounted
错误
是的,可滥用:
@safe unittest //编译
{
void escape (ref ubyte[] arr, ref ubyte[64] local)
{
arr = local;
}
@safe ubyte[] getPtr ()
{
ubyte[64] blob;
ubyte[] arr;
escape(arr, blob);
return arr; // 就像c时代.
}
}
opAssign
按值,而非按引用
.
准备实现侵改:
@safe unittest
{
@safe void assignCrap(RefCounted!(int*) countedRef)
{
int local;
auto anotherRef = countedRef;
//anotherRef生命期更短.
anotherRef.refCountedPayload() = &local; // ...这样,这会编译
}
}
编译器
未实现,可能refCountedPayload
中的return
属性,使得,生命期(负载)<生命期(anotherRef)
.
可能,构+成员函数
可侵改
.
这是有问题的:
@safe unittest
{
auto rc = RefCounted!int(123);
(ref int n) {
destroy(rc);
int oops = n; // 释放后使用.
}(rc.refCountedPayload);
}
为确保不在@safe
中发生,refCountedPayload
和析构器
必须是@system
.
简单变体:
@safe unittest
{
auto rc = RefCounted!int(123);
int* ptr = &rc.refCountedPayload();
destroy(rc);
int oops = *ptr;
}
问题是,允许域前
析构rc
.只能在生命期
性调用析构器算@safe
,其余不应@safe
.在@live
中可安全,其他则不是.
对-dip1000
也会:
@safe void abuse(Container)()
{ auto cont = Container([1,2,3]);
scope ptr = &cont.front;
destroy(cont);
int oops = *ptr;
}
可基于回调
的api
auto ref apply(alias callback, T)(auto ref RefCounted!T this_)
{
//确保在`callback`返回前保持引用
auto hold = this_;
//显式`域`防止`callback`逃逸引用
scope ptr = () @trusted { return &this_.refCountedPayload(); }();
return callback(*ptr);
}
// Usage
@safe unittest
{
auto rc = RefCounted!int(123);
rc.apply!((ref n) {
destroy(rc);
int ok = n; // 仍活着.
});
}
缺点
是:1,语法不好.2,额外引用计数
.
在真正所有权/借用
前(@live
不算),提供2个版本:
@safe
为愿意接受运行时成本
用户提供基于回调
的API
,及@system
提供直接
访问的API
,并让用户有责任不创建悬空引用
.
即,使refCountedPayload为@system
,加个@safe
的apply
.
有人destroy
了两次,怎么办?
析构器应重置_refCounted
为RefCountedStore.init
,确保即使使用destroy!false
,它也是幂等
的.
让refCountedPayload
在@safe
中工作,析构器
为私有,并定义noEarlyDestroy
属性,
对注解为noEarlyDestroy
的destroy
应为@system
.
有人提出.
由于应阻止安全访问私字段而绕过.
__traits(getMember)
破坏@safe
的所有东西,可以假装它不存在.
__traits(getMember)
很难修复,也是@system
变量动机,用来阻塞@safe
访问不依赖private
的代码.
我们不应让@安全
破坏私
.同时,不应加依赖私
的内存安全
特征.
DIP1035
可解决大部分__traits(getmember)
,或许@safe
访问私
会成为特性
而不是漏洞
.
不一致,在Nullable
上与RefCounted
上用apply
,前者不做啥,后者初化并触发断定,不一致.
重命名更好.因为两个版本
处理回调
返回值方式
不同.
rc.borrow!((scope ref payload) { /* ... */ });
rc.withPayload!((scope ref payload) { /* ... */ });
borrow
不错.
用-preivew=dip1000
编译.
发现问题:
虽然确实可使dirEntries
为模板
来解决链接
问题,即使模板化
它和或添加
析构器,但DirIterator
有些生成析构器
不会消失
.尝试链接的符号在后者中变化
了,仅此而已.目前在找原因.
struct _DirIteratorTempl()
{
//实现
}
alias DirIterator = _DirIteratorTempl!();
auto dirEntries()(string path, SpanMode mode, bool followSymlink = true)
{
//工作,无链接错误.
return _DirIteratorTempl!()(path, mode, followSymlink);
}
auto dirEntries()(string path, SpanMode mode, bool followSymlink = true)
{
//报错,好像_DirIteratorTempl不是模板!
return DirIterator(path, mode, followSymlink);
}
未记录DirIterator
,可修改,算运气了.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现