系统编程语言Rust使用ownership范式,以确保在必须释放资源时,在编译时以零成本运行.

在C++中,我们通常使用智能指针来实现隐藏资源管理的复杂性的相同目标.不过有几个不同之处:

  • 在 rust 蚀中,只有一个所有者,而C++ shared_ptr可以很容易泄漏所有权.
  • 在 rust 迹中,我们可以borrow 我们不拥有的引用,而C++ unique_ptr不能通过weak_ptrlock()以安全的方式共享.
  • 参考计数shared_ptr是昂贵的.

我的问题是:我们如何在以下约束中模拟C++中的所有权范型:

  • 任何时候只有一个所有者
  • 可以borrow 一个指针并暂时使用它,而不必担心资源超出范围(observer_ptr对这个没用)
  • 尽可能多的编译时判断.

Edit:考虑到目前为止的 comments ,我们可以得出结论:

  • 在编译器中没有对这一点的编译时支持(我希望有大约decltype个我不知道的模板魔法).可能在其他地方使用静态分析(污染?)

  • 如果没有引用计数,就不可能得到这个.

  • 没有标准的实现来区分shared_ptr和拥有或borrow 语义

  • 可以通过创建shared_ptrweak_ptr左右的包装器类型来实现:

    • owned_ptr:不可复制,移动语义,封装共享\u ptr,访问borrowed_ptr
    • borrowed_ptr:可复制,封装weak_ptr,锁定方法
    • locked_ptr:不可复制,移动语义,封装了锁weak_ptr中的shared_ptr

推荐答案

编译时判断根本无法做到这一点.C++类系统缺乏任何理由来解释当对象超出范围、移动或销毁时,更不用说将其转换为类型约束.

您可以做的是使用一个unique_ptr的变体,它会保留一个计数器,指示在运行时有多少"borrow "处于活动状态.它不会返回一个原始指针,而是返回一个智能指针,在构造时递增计数器,在销毁时递减计数器.如果unique_ptr在计数非零时被销毁,至少你知道某个地方有人做错了什么.

然而,这并不是一个万无一失的解决方案.不管你如何努力阻止它,总会有办法得到一个指向底层对象的原始指针,然后游戏就结束了,因为原始指针很容易比智能指针和unique_ptr更长寿.有时甚至需要获取原始指针,以便与需要原始指针的API交互.

此外,ownership is not about pointers.Box/unique_ptr允许堆分配一个对象,但与将同一个对象放在堆栈上(或放在另一个对象内,或其他任何地方)相比,它对所有权、生存时间等没有任何改变.为了在C++中使用这样的系统,你必须对所有对象都做这样的"borrow 计数"包装,而不仅仅是unique_ptrs.这是非常不切实际的.

因此,让我们重新讨论编译时选项.C++编译器不能帮助我们,但也许皮尔斯可以吗?从理论上讲,如果实现类型系统的整个生命周期部分,并将注释添加到所使用的所有API中(除了自己的代码之外),这可能会起作用.

但它需要对整个程序中使用的所有函数进行注释.包括第三方图书馆的私有助手功能.以及那些没有可用源代码的.对于那些实现过于复杂,令linter无法理解的人(从Rust 的经验来看,有时某些东西是安全的原因过于微妙,无法在生命周期的静态模型中表达,必须以稍微不同的方式编写,以帮助编译器).在最后两个例子中,linter无法验证注释是否确实正确,因此您可以重新信任程序员.此外,有些API(或者更确切地说,它们何时安全的条件)不能在Rust使用的生命周期系统中很好地表达.

换言之,对于这一点,一个完整且实际有用的过梁将是具有相关失败风险的大量原始研究.

也许有一个折衷方案,可以以20%的成本获得80%的yield ,但既然你想要一个硬保证(老实说,我也希望如此),那就太不走运了.现有的"好实践"在C++中已经有很长的路要走,以最小化风险,通过基本思维(和记录) rust 程序员的方式,只是没有编译器的帮助.考虑到C++的状态和它的生态系统,我不确定是否有很多改进.

tl;dr Just use Rust;-)


下面是一些例子,人们试图模仿C++中RITE所有权范型的部分,但取得了有限的成功:

Rust相关问答推荐

如何从Rust记录WASM堆内存使用情况?

当Option为None时,Option数组是否占用Rust中的内存?

什么是Rust惯用的方式来使特征向量具有单个向量项的别名?

如何创建引用构造函数拥有的变量的对象?

Arrow RecordBatch as Polars DataFrame

如何实现泛型枚举的`Serde::Desialize`特性

Pin<;&;mut可能将Uninit<;T>;>;合并为Pin<;&;mut T>;

了解Rust';s特征对象和不同函数签名中的生存期注释

带引脚和不带引脚的比较功能

Rust Option 的空显式泛型参数

面临意外的未对齐指针取消引用:地址必须是 0x8 的倍数,但为 0x__错误

Rust并发读写引起的死锁问题

如何将 C++ 程序链接到 Rust 程序,然后将该 Rust 程序链接回 C++ 程序? (cpp -> rust -> cpp)

在 Rust 中,Weak 如何知道内部值何时被删除?

Rust/Serde/HTTP:序列化`Option`

是否可以在 Rust 中的特定字符上实现特征?

为什么拥有 i32 所有权的函数需要它是可变的?

在 Rust 中退出进程

如何构建包含本地依赖项的 docker 镜像?

为什么这里需要类型注解?