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 值的行为与上述示例中的行为类似.您或其他人默认创建的每个结构或枚举的值将被移动。

另一个重要的事情是你可以给每个类型一个析构函数,也就是说,当这个类型的值超出范围并被销毁时调用的一段代码。例如,与VecBox 关联的析构函数将释放相应的内存。可以通过实现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。另一方面,像VecBox 这样的结构不是Copy——它们故意不实现它,因为它们的字节复制会导致双重释放,因为它们的析构函数可以在同一个指针上运行两次。

上面的Copy 有点离题,只是为了提供更清晰的图片。 Rust 中的所有权基于移动语义。当我们说某个值拥有某些东西时,例如“Box<T> 拥有给定的T”,我们的意思是它们之间的语义联系,而不是某种神奇的东西或语言中内置的东西。只是像 VecBox 这样的大多数值都没有实现 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,它们用于BoxVec等容器类型的实现。如果不是 Drop impl,BoxVec 可能完全是 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功能呢?

wpf中资源文件字典如何使它也拥有cs文件

对 Compositions.Sections 中任何资源的引用。我如何知道这些引用的资源类型?

Rust—数据类型

同一进程中的线程究竟共享哪些资源

MAPGIS数据类型