理解 Rust 中的智能指针
Understand smart pointers in Rust
我是 Rust 的新手,写作是为了理解 Rust 中的 "Smart pointers"。我对智能指针在 C++ 中的工作原理有基本的了解,并且从几年前就开始使用它进行内存管理。但令我非常惊讶的是,Rust 还 明确地 提供了这样的实用程序。
因为这里有一个教程(https://pcwalton.github.io/2013/03/18/an-overview-of-memory-management-in-rust.html), it seems that every raw pointers have been automatically wrapped with a smart pointer, which seems very reasonable. Then why do we still need such Box<T>
, Rc<T>
, and Ref<T>
stuff? According to this specification: https://doc.rust-lang.org/book/ch15-00-smart-pointers.html
任何意见将不胜感激。谢谢
您可以将 T
和 Box<T>
之间的区别视为静态分配对象和动态分配对象之间的区别(后者是通过 new
创建的) C++ 术语中的表达式)。
在 Rust 中,T
和 Box<T>
都代表一个对引用对象具有 所有权 的变量(即当变量超出范围时,该对象将被销毁,无论它是按值存储还是按引用存储)。相反,&T
和&mut T
代表借用对象(即这些变量不负责对象的销毁,不能超过对象的所有者)对象)。
默认情况下,您可能希望使用 T
,但有时您可能希望(或不得不)使用 Box<T>
。例如,如果您想要拥有一个太大而无法就地分配的 T
,您可以使用 Box<T>
。当对象根本没有已知大小时,您也会使用它,这意味着您存储或传递它的唯一选择是通过“指针”(Box<T>
)。
在 Rust 中,一个对象通常是可变的或别名的,但不是两者都是。如果你给出了一个对象的不可变引用,你通常需要等到这些引用结束才能再次改变该对象。
此外,Rust 的不变性是可传递的。如果您以不可变方式接收一个对象,则意味着您可以以不可变方式访问其内容(以及这些内容的内容等)。
通常,所有这些都是在编译时强制执行的。这意味着您可以更快地发现错误,但您只能表达编译器可以静态证明的内容。
与T
和Box<T>
一样,您有时可能会使用RefCell<T>
,这是另一种所有权类型。但与 T
和 Box<T>
不同,RefCell<T>
在运行时 而不是编译时强制执行借用检查规则 ,这意味着有时您可以使用它是安全的,但不会通过编译器的静态借用检查器。这方面的主要示例是获取对不可变接收的对象内部的可变引用(根据 Rust 的静态强制规则,这将使整个内部不可变)。
类型 Ref<T>
和 RefMut<T>
分别是 &T
和 &mut T
的运行时检查等效项。
(编辑:这整件事有点像个谎言。&mut
真正的意思是“唯一借用”,&
意思是“非唯一借用”。某些类型,如互斥量,可以是非唯一但仍然可变借用,否则它们将毫无用处。)
Rust 的所有权模型试图促使您编写在编译时已知对象生命周期的程序。这在某些情况下效果很好,但会使其他情况难以或无法表达。
Rc<T>
及其原子兄弟 Arc<T>
是 T
的引用计数包装器。他们为您提供所有权模式的替代方案。
当您想要使用和正确处理一个对象时,它们很有用,但是在您编写代码时,不容易(或不可能)确定哪个特定变量应该是该对象的所有者对象(因此应该注意处理它)。很像在 C++ 中,这意味着对象没有单一所有者,对象将由指向它的最后一个引用计数包装器处理。
您链接的文章使用了过时的语法。某些智能指针曾经有特殊的名称和相关语法,这些名称和相关语法在 Rust 1.0 之前的某个时间已经被删除:
Box<T>
替换为 ~T
("owned pointers")
Rc<T>
替换为 @T
("managed pointers")
因为 Internet 永远不会忘记,您仍然可以找到使用旧语法的 1.0 版之前的文档和文章(例如您链接的文章)。检查文章的日期:如果是在 2015 年 5 月之前,您正在处理一个早期的、不稳定的 Rust。
我是 Rust 的新手,写作是为了理解 Rust 中的 "Smart pointers"。我对智能指针在 C++ 中的工作原理有基本的了解,并且从几年前就开始使用它进行内存管理。但令我非常惊讶的是,Rust 还 明确地 提供了这样的实用程序。
因为这里有一个教程(https://pcwalton.github.io/2013/03/18/an-overview-of-memory-management-in-rust.html), it seems that every raw pointers have been automatically wrapped with a smart pointer, which seems very reasonable. Then why do we still need such Box<T>
, Rc<T>
, and Ref<T>
stuff? According to this specification: https://doc.rust-lang.org/book/ch15-00-smart-pointers.html
任何意见将不胜感激。谢谢
您可以将 T
和 Box<T>
之间的区别视为静态分配对象和动态分配对象之间的区别(后者是通过 new
创建的) C++ 术语中的表达式)。
在 Rust 中,T
和 Box<T>
都代表一个对引用对象具有 所有权 的变量(即当变量超出范围时,该对象将被销毁,无论它是按值存储还是按引用存储)。相反,&T
和&mut T
代表借用对象(即这些变量不负责对象的销毁,不能超过对象的所有者)对象)。
默认情况下,您可能希望使用 T
,但有时您可能希望(或不得不)使用 Box<T>
。例如,如果您想要拥有一个太大而无法就地分配的 T
,您可以使用 Box<T>
。当对象根本没有已知大小时,您也会使用它,这意味着您存储或传递它的唯一选择是通过“指针”(Box<T>
)。
在 Rust 中,一个对象通常是可变的或别名的,但不是两者都是。如果你给出了一个对象的不可变引用,你通常需要等到这些引用结束才能再次改变该对象。
此外,Rust 的不变性是可传递的。如果您以不可变方式接收一个对象,则意味着您可以以不可变方式访问其内容(以及这些内容的内容等)。
通常,所有这些都是在编译时强制执行的。这意味着您可以更快地发现错误,但您只能表达编译器可以静态证明的内容。
与T
和Box<T>
一样,您有时可能会使用RefCell<T>
,这是另一种所有权类型。但与 T
和 Box<T>
不同,RefCell<T>
在运行时 而不是编译时强制执行借用检查规则 ,这意味着有时您可以使用它是安全的,但不会通过编译器的静态借用检查器。这方面的主要示例是获取对不可变接收的对象内部的可变引用(根据 Rust 的静态强制规则,这将使整个内部不可变)。
类型 Ref<T>
和 RefMut<T>
分别是 &T
和 &mut T
的运行时检查等效项。
(编辑:这整件事有点像个谎言。&mut
真正的意思是“唯一借用”,&
意思是“非唯一借用”。某些类型,如互斥量,可以是非唯一但仍然可变借用,否则它们将毫无用处。)
Rust 的所有权模型试图促使您编写在编译时已知对象生命周期的程序。这在某些情况下效果很好,但会使其他情况难以或无法表达。
Rc<T>
及其原子兄弟 Arc<T>
是 T
的引用计数包装器。他们为您提供所有权模式的替代方案。
当您想要使用和正确处理一个对象时,它们很有用,但是在您编写代码时,不容易(或不可能)确定哪个特定变量应该是该对象的所有者对象(因此应该注意处理它)。很像在 C++ 中,这意味着对象没有单一所有者,对象将由指向它的最后一个引用计数包装器处理。
您链接的文章使用了过时的语法。某些智能指针曾经有特殊的名称和相关语法,这些名称和相关语法在 Rust 1.0 之前的某个时间已经被删除:
Box<T>
替换为~T
("owned pointers")Rc<T>
替换为@T
("managed pointers")
因为 Internet 永远不会忘记,您仍然可以找到使用旧语法的 1.0 版之前的文档和文章(例如您链接的文章)。检查文章的日期:如果是在 2015 年 5 月之前,您正在处理一个早期的、不稳定的 Rust。