首页 文章

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

提问于
浏览
4

当有一个指向某个堆分配内存的框指针时,我假设Rust拥有'hardcoded'所有权知识,因此当通过调用某个函数传输所有权时,将移动资源并且函数中的参数是新所有者 .

但是,例如,对于矢量,这是怎么发生的?他们的资源和所有权机制也适用于盒子指针 - 但它们是存储在变量本身的常规值,而不是指针 . 在这种情况下,Rust(知道)如何应用所有权机制?

Can I make my own type which owns resources?

2 回答

  • 12

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

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

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

    在其核心中,Rust的所有权机制非常简单,并且具有非常简单的规则 .

    首先,让我们来定义一下这是什么 . 这很简单 - 当一个值以新名称变为可用并且在旧名称下停止可用时,该名称将被移动:

    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 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 trait来选择退出不可复制性,该特征将该类型标记为可自动复制 - 其值将不再被移动而是被复制:

    #[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中的所有权基于移动语义 . 当我们说某些 Value 拥有某些东西时,比如“ 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 代表的外国资源 .

相关问题