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



当一个框指针指向某个堆分配的内存时,我假设Rust具有所有权的"硬编码"知识,因此当通过调用某个函数转移所有权时,资源被移动,函数中的参数是新所有者。

然而,例如,向量是如何发生这种情况的?它们也"拥有"自己的资源,所有权机制就像框指针一样适用——但它们是存储在变量本身中的常规值,而不是指针。Rust(知道(如何在这种情况下应用所有权机制?

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

tl;dr:Rust中的"拥有"类型并不是什么魔法,它们肯定不会硬编码到编译器或语言中。它们只是以某种方式编写的类型(不实现Copy,可能有析构函数(,并且具有通过不可复制性和析构函数强制执行的某些语义。

在其核心中,Rust的所有权机制非常简单,规则也非常简单。

首先,让我们定义什么是move。它很简单-当一个值在新名称下可用并在旧名称下停止可用时,它被称为move

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

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

另一件重要的事情是,你可以给每个类型一个析构函数,也就是说,当这个类型的值超出范围并被销毁时,就会调用一段代码。例如,与VecBox关联的析构函数将释放相应的内存。析构函数可以通过实现Drop特性来声明:

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

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

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

最新更新