【问题标题】:Understand smart pointers in Rust理解 Rust 中的智能指针
【发布时间】:2019-03-09 08:33:30
【问题描述】:

我是 Rust 的新手,写作是为了理解 Rust 中的“智能指针”。我对智能指针在 C++ 中的工作原理有基本的了解,并且从几年前就开始使用它进行内存管理。但令我非常惊讶的是,Rust 还明确地提供了这样的实用程序。

因为从这里的教程(https://pcwalton.github.io/2013/03/18/an-overview-of-memory-management-in-rust.html)来看,似乎每个原始指针都被自动包装了一个智能指针,这似乎很合理。那为什么我们还需要Box<T>Rc<T>Ref<T>这些东西呢?根据本规范:https://doc.rust-lang.org/book/ch15-00-smart-pointers.html

任何 cmets 都会受到赞赏。谢谢。

【问题讨论】:

  • “似乎每个原始指针都被自动包装智能指针”——不,这听起来像是到处都在发生隐式转换。原始指针仍然存在,但它们从未自动使用过,它们的大部分用例已替换为某种智能指针风格,其中Rc<T>, Box<T>Ref<T> 是示例。
  • “为什么我们还需要Box<T>Rc<T>Ref<T>这些东西?” ——我不明白这个问题。那些智能指针。除了“明确”之外,您还会如何使用它们?听起来你在问c++,“我知道智能指针是什么以及如何使用它们,但我不知道我们需要unique_ptrshared_ptr 来做什么。”
  • @trentcl 不,我的意思是这样。编译器是否使用这三个来实际实现智能指针?
  • 编译器是否使用智能指针来实现智能指针?对不起,我还是不明白。智能指针是在 Rust 中使用原始指针实现的,就像在 C++ 中一样。您可以查看source code for Rc<T>,例如:它包含一个NonNull,它只是一个围绕*const(原始)指针的wrapper。这一切都在标准库中,而不是在编译器本身中。
  • 如果第一篇文章的语法让你感到困惑,那是因为它已经很老了。我为此写了一个答案。保留以前的 cmets 以防我错了

标签: rust smart-pointers


【解决方案1】:

您可以将TBox<T> 之间的区别视为静态分配对象和动态分配对象之间的区别(后者是通过C++ 术语中的new 表达式创建的)。

在 Rust 中,TBox<T> 都表示一个对所指对象拥有所有权的变量(即,当变量超出范围时,该对象将被销毁,无论它是按值或引用存储)。相反,&T&mut T 代表对象的借用(即这些变量不负责销毁对象,它们不能比对象的所有者更长寿)。

默认情况下,您可能希望使用T,但有时您可能希望(或必须)使用Box<T>。例如,如果您想拥有一个太大而无法就地分配的T,您可以使用Box<T>。当对象根本没有已知大小时,您也可以使用它,这意味着您存储或传递它的唯一选择是通过“指针”(Box<T>)。


在 Rust 中,一个对象通常要么是可变的,要么是别名的,但不能两者兼而有之。如果你已经给出了一个对象的不可变引用,你通常需要等到这些引用结束之后才能再次改变那个对象。

此外,Rust 的不变性是可传递的。如果你收到一个不可变的对象,这意味着你可以访问它的内容(以及这些内容的内容,等等)也是不可变的。

通常,所有这些事情都是在编译时强制执行的。这意味着您可以更快地捕获错误,但您只能表达编译器可以静态证明的内容。

TBox<T> 一样,您有时可以使用RefCell<T>,这是另一种所有权类型。但与TBox<T> 不同,RefCell<T> 在运行时而不是编译时强制执行借用检查规则,这意味着有时您可以用它做一些安全但不会通过的事情编译器的静态借用检查器。这方面的主要示例是获取对一个对象内部的可变引用,该对象被不可变地接收(根据 Rust 的静态强制规则,这将使整个内部不可变)。

Ref<T>RefMut<T> 类型分别是 &T&mut T 的运行时检查等效项。

(编辑:这整件事有点谎言。&mut 真正的意思是“唯一借用”,& 意味着“非唯一借用”。某些类型,如互斥锁,可以是非唯一的,但仍然是可变的借来的,否则它们将毫无用处。)


Rust 的所有权模型试图推动您编写在编译时知道对象生命周期的程序。这在某些情况下效果很好,但会使其他情况难以或无法表达。

Rc<T> 和它的原子兄弟Arc<T>T 的引用计数包装器。它们为您提供了所有权模型的替代方案。

当您想要使用和正确处置一个对象时,它们很有用,但在您编写代码的那一刻,确定哪个特定变量应该是该对象的所有者并不容易(或不可能)(因此应该注意处理它)。就像在 C++ 中一样,这意味着对象没有单一的所有者,并且该对象将由指向它的最后一个引用计数包装器处理。

【讨论】:

  • T应该先分配到栈上,然后传递给Box::new(),那么如果T很大,通过Box没有帮助?
【解决方案2】:

您链接的文章使用过时的语法。在 Rust 1.0 之前的一段时间内,某些智能指针曾经具有特殊名称和相关语法,已被删除:

  • Box<T> 替换 ~T(“拥有的指针”)
  • Rc<T> 替换 @T(“托管指针”)

因为互联网永远不会忘记,您仍然可以找到使用旧语法的 1.0 之前的文档和文章(例如您链接的文章)。检查文章的日期:如果是在 2015 年 5 月之前,您正在处理早期的、不稳定的 Rust。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-12
    • 2017-04-29
    • 1970-01-01
    相关资源
    最近更新 更多