Rust 如何知道哪些类型拥有资源?
Posted
技术标签:
【中文标题】Rust 如何知道哪些类型拥有资源?【英文标题】:How does Rust know which types own resources? 【发布时间】:2015-10-12 15:01:51 【问题描述】:当一个盒子指针指向某个堆分配的内存时,我假设 Rust 已经“硬编码”了所有权知识,因此当通过调用某个函数来转移所有权时,资源会移动并且函数中的参数是新的所有者。
但是,例如,矢量如何发生这种情况?它们也“拥有”自己的资源,所有权机制适用于盒子指针——但它们是存储在变量自身中的常规值,而不是指针。在这种情况下,Rust(知道)如何应用所有权机制?
我可以自己制作拥有资源的类型吗?
【问题讨论】:
我不确定我是否理解您的问题,但是当您将值放入向量时,该值将归向量所有。我认为如果您提供一个代码示例来说明您所要求的内容会很有帮助。 我现在没有时间写完整的答案,我只想提一下Box
没有特殊或硬编码。 (嗯,目前有些方面是,但没有一个与这个问题有任何关系,它们只是硬编码,因为用纯库代码表达这些东西的语言功能还没有完成。) Box 的所有权完全有效作为 Vec 的所有权。
@Adrian “但是当你将一个值放入一个向量中时,该值将由该向量拥有。” AFAIK 值不属于,资源 是。我不是在问向量中的数据,我问的是向量变量拥有内存,就像一个盒子一样——但它不是一个盒子。我基本上只是在询问 Rust 内部,即所有权适用于哪些构造,以及它是如何确定的。
@delnan 我认为所有权必须在语言中“烘焙”?如果你没有时间解释,你可能有一个链接吗?
当您创建一个新向量 (Vec::new
) 或推送到一个向量时,内存由该向量分配,例如在 this line 上。 Vec
实现了Drop
,它可以在向量被销毁时释放内存,这发生在this line。
【参考方案1】:
tl;dr:Rust 中的“拥有”类型并不是什么魔法,而且它们肯定不会硬编码到编译器或语言中。它们只是以某种方式编写的类型(不实现 Copy
并且可能具有析构函数)并且具有通过不可复制性和析构函数强制执行的某些语义。
在其核心,Rust 的所有权机制非常简单,规则也非常简单。
首先,让我们定义move是什么。这很简单——当一个值以新名称可用并且不再以旧名称可用时,就称它为 moved:
struct X(u32);
let x1 = X(12);
let x2 = x1;
// x1 is no longer accessible here, trying to use it will cause a compiler error
将值传递给函数时也会发生同样的情况:
fn do_something(x: X)
let x1 = X(12);
do_something(x1);
// x1 is no longer accessible here
请注意,这里 绝对没有魔法 - 只是默认情况下 every 类型的 every 值的行为与上述示例中的行为类似.您或其他人默认创建的每个结构或枚举的值将被移动。
另一个重要的事情是你可以给每个类型一个析构函数,也就是说,当这个类型的值超出范围并被销毁时调用的一段代码。例如,与Vec
或Box
关联的析构函数将释放相应的内存。可以通过实现Drop
trait 来声明析构函数:
struct X(u32);
impl Drop for X
fn drop(&mut self)
println!("Dropping ", x.0);
let x1 = X(12);
// x1 is dropped here, and "Dropping 12" will be printed
有一种方法可以通过实现 Copy
特征来选择退出不可复制性,该特征将类型标记为自动可复制 - 它的值将不再被移动而是被复制:
#[derive(Copy, Clone)] struct X(u32);
let x1 = X(12);
let x2 = x1;
// x1 is still available here
复制按字节完成 - x2
将包含 x1
的字节相同副本。
并非所有类型都可以制作Copy
- 只有那些具有Copy
内部并且不实现 Drop
的类型。所有原始类型(&mut
引用除外,但包括 *const
和 *mut
原始指针)在 Rust 中都是 Copy
,因此每个仅包含原始类型的结构都可以制作为 Copy
。另一方面,像Vec
或Box
这样的结构不是Copy
——它们故意不实现它,因为它们的字节复制会导致双重释放,因为它们的析构函数可以在同一个指针上运行两次。
上面的Copy
有点离题,只是为了提供更清晰的图片。 Rust 中的所有权基于移动语义。当我们说某个值拥有某些东西时,例如“Box<T>
拥有给定的T
”,我们的意思是它们之间的语义联系,而不是某种神奇的东西或语言中内置的东西。只是像 Vec
或 Box
这样的大多数值都没有实现 Copy
并因此移动而不是复制,并且它们还(可选地)有一个析构函数来清理这些类型可能为它们分配的任何东西(内存,套接字、文件等)。
鉴于上述情况,当然您可以编写自己的“拥有”类型。这是惯用的 Rust 的基石之一,标准库和外部库中的很多代码都是这样编写的。例如,一些 C API 提供了创建和销毁对象的函数。在 Rust 中围绕它们编写一个“拥有”包装器非常容易,并且可能非常接近您的要求:
extern
fn create_widget() -> *mut WidgetStruct;
fn destroy_widget(w: *mut WidgetStruct);
fn use_widget(w: *mut WidgetStruct) -> u32;
struct Widget(*mut WidgetStruct);
impl Drop for Widget
fn drop(&mut self)
unsafe destroy_widget(self.0);
impl Widget
fn new() -> Widget Widget(unsafe create_widget() )
fn use_it(&mut self) -> u32
unsafe use_widget(self.0)
现在你可以说Widget
拥有一些由*mut WidgetStruct
代表的外国资源。
【讨论】:
请注意,原始类型集还包括原始指针* mut T
和* const T
,它们用于Box
和Vec
等容器类型的实现。如果不是 Drop
impl,Box
和 Vec
可能完全是 Copy
- 它只是 unsafe
并且在语义上是错误的。
由于它经常让人绊倒,请注意在运行时移动和复制是相同的——只有类型检查器知道区别。两者最终都是浅memcpy
。
@VladimirMatveev 我有一个关于借款的new question 和drop
,如果你有兴趣:)【参考方案2】:
这是另一个值如何拥有内存并在值被销毁时释放它的示例:
extern crate libc;
use libc::malloc, free, c_void;
struct OwnerOfMemory
ptr: *mut c_void
impl OwnerOfMemory
fn new() -> OwnerOfMemory
OwnerOfMemory
ptr: unsafe malloc(128)
impl Drop for OwnerOfMemory
fn drop(&mut self)
unsafe free(self.ptr);
fn main()
let value = OwnerOfMemory::new();
【讨论】:
直接从libc
crate: use libc::malloc, free, c_void
调用use
函数可能更惯用。
@VladimirMatveev 谢谢,我已经编辑了我的答案。我不知道这是可能的。以上是关于Rust 如何知道哪些类型拥有资源?的主要内容,如果未能解决你的问题,请参考以下文章
2022-10-29:go语言中的defer能非常方便地处理资源释放问题,rust语言里如何实现defer功能呢?