Ada 的内存安全性与 Rust 相比如何?

本文比较了 Rust 和 Ada 在防止常见内存相关错误方面的表现,主要内容如下:

  • 常见内存相关错误:包括内存泄漏、缓冲区溢出、使用后释放、双重释放、竞态条件、悬空指针、释放栈内存等。

    • 内存泄漏:Rust 较难导致内存泄漏,变量超出作用域会自动释放内存,引用计数智能指针在引用计数为 0 时自动释放内存;Ada 中需手动调用Ada.Unchecked_Deallocation过程释放动态分配的内存,指定存储池大小可自动释放内存。
    • 缓冲区溢出:Rust 和 Ada 都支持运行时边界检查,访问越界数组索引会引发“panic”或Constraint_Error异常;SPARK 可通过形式验证证明缓冲区溢出不会发生。
    • 使用后释放:Rust 的借用检查器可防止使用已释放的值,调用drop会移动变量;Ada 中访问已释放的指针会引发Constraint_Error异常,可通过指定“非空排除”约束避免。
    • 双重释放:Rust 的借用检查器可防止同一变量被多次释放;Ada 中释放空指针无影响,创建指针别名可能导致双重释放并引发Program_Error异常,SPARK 不允许使用Unchecked_Deallocation创建一般访问类型来避免双重释放。
    • 竞态条件:多线程程序中, race condition 可能导致程序结果依赖线程执行顺序,常见的有 TOC/TOU 错误。Rust 通过互斥锁(Mutex)和原子引用计数智能指针(Arc<T>)来防止竞态条件,Ada 通过受保护对象(Protected Objects)来防止数据竞争。
    • 悬空指针:Rust 难以创建悬空指针,Ada 通过访问级别(accessibility levels)机制防止悬空指针,可通过Unchecked_Access忽略该机制,但可能导致错误。
    • 释放栈内存:尝试释放栈上分配的对象会导致程序出错,Rust 自动释放超出作用域的变量,Ada 中一般访问类型可指向栈上分配的对象,释放时会引发Program_Error异常。
  • 对 Ada 是否公平的讨论:Ada 在内存安全方面虽稍逊于 Rust,但在强类型、直观的低级编程语义和静态分析能力方面仍有优势,如可在不使用指针或堆内存的情况下完成很多操作,支持栈上分配的可变长度数组等。
  • 结论:Rust 在防止常见错误方面表现出色,虽语义复杂但设计先进;Ada 隐藏了复杂机制,经数十年在安全关键系统中的使用验证可靠。希望未来的编程语言能借鉴两者的优点。

引用了 ISO/IEC 9899 (2018) Information technology — Programming languages — C等参考文献,还对一些代码示例中的细节进行了注释说明。

阅读 5
0 条评论