所有权与生命周期

原文跟踪ownership.md   Commit: 0e6c680ebd72f1860e46b2bd40e2a387ad8084ad

所有权是Rust的突破性特征。 它允许Rust完全内存安全且高效,同时避免垃圾回收。 在详细介绍所有权制度之前,我们将考虑这种设计的动机。

我们假设您接受垃圾收集(GC)并不总是最佳解决方案,并且希望在某些上下文中手动管理内存。 如果你不接受这个,你可能会对另一种语言对感兴趣?

无论您对GC的感受如何,显然对于使代码安全无疑是一个巨大的好处。 你永远不必担心过早的事情会消失(尽管你是否仍然想要指出那件事是另一个问题......)。 这是C和C ++程序需要处理的普遍问题。 考虑一下这个简单的错误,即我们所有使用非GC语言的人都曾在某个方面做过:

fn as_str(data: &u32) -> &str { // compute the string let s = format!("{}", data); // OH NO! We returned a reference to something that // exists only in this function! // Dangling pointer! Use after free! Alas! // (this does not compile in Rust) &s }

这正是Rust的所有权系统要解决的问题。 Rust知道&s的生存范围,因此可以防止它逃逸。 然而,这是一个简单的案例,即使是C编译器也可以合理地捕获。 随着代码变得越来越大,指针通过各种函数被提供,事情变得越来越复杂。 最终,C编译器将崩溃,并且无法执行足够的转义分析来证明您的代码不健全。 因此,在假设它是正确的情况下,它将被迫接受您的程序。

这将永远不会发生在Rust。 程序员可以向编译器证明一切都是正确的。

当然,Rust关于所有权的故事要比仅仅验证引用不会超出其引用范围复杂。 那是因为确保指针始终有效要比这复杂得多。 例如,在此代码中,

let mut data = vec![1, 2, 3]; // get an internal reference let x = &data[0]; // OH NO! `push` causes the backing storage of `data` to be reallocated. // Dangling pointer! Use after free! Alas! // (this does not compile in Rust) data.push(4); println!("{}", x);

天真的范围分析不足以防止这个错误,因为数据确实存在,只要我们需要。 然而,当我们引用它时它被改变了。 这就是Rust要求任何引用来冻结引用对象及其所有者的原因。