Rust 如何知道哪些类型拥有资源?

How does Rust know which types own resources?

当一个盒子指向某个堆分配的内存时,我假设 Rust 具有 'hardcoded' 所有权知识,因此当通过调用某个函数转移所有权时,资源是 moved 并且函数中的参数是新所有者。

但是,例如,向量是如何发生的?他们也 'own' 他们的资源,所有权机制适用于盒子指针——但它们是存储在变量 自身 中的常规值,而不是指针。 Rust 如何(知道)在这种情况下应用所有权机制?

我可以制作自己的拥有资源的类型吗?

tl;dr:"owning" 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

请注意,这里 绝对没有魔法 - 只是默认情况下 everyevery type 的行为与上面的例子一样。您或其他人默认创建的每个结构或枚举的值将被移动。

另外一个重要的事情是,你可以给每个类型一个析构函数,也就是当这个类型的值超出范围被销毁时调用的一段代码.例如,与 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 ,因此移动而不是复制,它们也(可选)有一个析构函数来清理这些类型可能有的任何东西为它们分配(内存、套接字、文件等)。

鉴于以上所述,当然您可以编写自己的 "owning" 类型。这是惯用的 Rust 的基石之一,标准库和外部库中的很多代码都是用这种方式编写的。例如,一些 C API 提供了创建和销毁对象的函数。在 Rust 中围绕它们编写 "owning" 包装器非常容易,它可能非常接近您的要求:

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为代表的外国资源。

这是另一个值如何拥有内存并在值被销毁时释放内存的示例:

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();
}