C ++有三种方式将参数传递给函数:按值,由左值参考,并且通过右值参考。其中,按值传递会在被调用函数收到其自己的副本的意义上创建所有权,而按右值引用传递表示值可能已被使用,即调用者将不再使用它。通过左值引用传递意味着该对象是临时从调用方借用的。
但是,这些往往是“按惯例”的,不能始终由编译器检查。并且您可能会意外地将左值引用转换为右值引用std::move()
。具体来说,存在三个问题:
引用可以超过其引用的对象。Rust的生命周期系统可以防止这种情况。
任何时候都可以有一个以上的可变/非常量引用活动。Rust的借阅检查器可以防止这种情况。
您不能选择退出参考。如果不知道被调用函数的签名,就无法在调用站点上看到该函数是否创建了对对象的引用。因此,不能通过删除类的任何特殊方法或通过审核呼叫站点是否符合某些“无引用”样式指南来可靠地阻止引用。
生存期问题与基本内存安全性有关。当引用的对象过期时,使用引用当然是非法的。但是,当您将引用存储在对象中时,尤其是当该对象超出当前范围时,很容易忘记生命周期。C ++类型系统无法解决这个问题,因为它根本无法建模对象的生存期。
该std::weak_ptr
智能指针确实类似于一个普通的参考编码所有权语义,但是需要被引用的对象经由管理shared_ptr
,即,是引用计数。这不是零成本的抽象。
虽然C ++具有const系统,但它不会跟踪是否可以修改对象,而是跟踪是否可以通过该特定引用来修改对象。这不能为“无畏并发”提供足够的保证。相反,Rust保证,如果有一个活动的可变引用是唯一的引用(“我是唯一可以更改此对象的人”),并且如果存在不可更改的引用,那么对该对象的所有引用都是不可更改的(“虽然我可以从对象中读取内容,但是没有人可以更改它”)。
在C ++中,您可能很想通过带有互斥量的智能指针来保护对对象的访问。但是如上所述,一旦有了参考,它就可以逃脱其预期的使用寿命。因此,这样的智能指针不能保证它是对其管理对象的单点访问。这样的方案实际上可能在实践中可行,因为大多数程序员都不想破坏自己,但是从类型系统的角度来看,这仍然是完全不合理的。
智能指针的普遍问题是它们是核心语言之上的库。核心语言功能集支持这些智能指针,例如std::unique_ptr
需要移动构造函数。但是它们不能解决核心语言中的缺陷。调用函数时隐式创建引用以及将引用悬挂在一起的能力意味着核心C ++语言不健全。无法将可变引用限制为单个引用意味着C ++不能保证在任何类型的并发情况下针对竞争条件的安全性。
当然,在许多方面,C ++和Rust比不相似时更相似,尤其是在它们静态确定的对象生存期的概念上。但是,尽管可以编写正确的C ++程序(只要没有程序员会犯任何错误),Rust可以保证所讨论属性的正确性。