当有一个指向某个堆分配内存的框指针时,我假设Rust拥有'hardcoded'所有权知识,因此当通过调用某个函数传输所有权时,将移动资源并且函数中的参数是新所有者 .
但是,例如,对于矢量,这是怎么发生的?他们的资源和所有权机制也适用于盒子指针 - 但它们是存储在变量本身的常规值,而不是指针 . 在这种情况下,Rust(知道)如何应用所有权机制?
Can I make my own type which owns resources?
下面是一个值如何拥有内存并在值被销毁时释放它的另一个示例:
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(); }
Rust中的tl; dr:"owning"类型不是一些魔术,它们肯定不会硬编码到编译器或语言中 . 它们只是以某种方式编写的类型(不实现 Copy 并且可能具有析构函数)并且具有通过不可复制性和析构函数强制执行的某些语义 .
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
请注意,这里绝对没有魔法 - 默认情况下,默认情况下,每种类型的每个值的行为与上面的示例相同 . 默认情况下,您或其他人创建的每个结构或枚举的值都将被移动 .
另一个重要的事情是你可以给每个类型一个析构函数,也就是说,当这个类型的值超出范围并被销毁时调用的一段代码 . 例如,与 Vec 或 Box 关联的析构函数将释放相应的内存 . 可以通过实现 Drop trait来声明析构函数:
Vec
Box
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 trait来选择退出不可复制性,该特征将该类型标记为可自动复制 - 其值将不再被移动而是被复制:
#[derive(Copy, Clone)] struct X(u32); let x1 = X(12); let x2 = x1; // x1 is still available here
副本以字节方式完成 - x2 将包含 x1 的字节相同副本 .
x2
x1
不是每种类型都可以制作 Copy - 只有那些内部 Copy 并且不实现 Drop 的那些类型 . 所有原始类型(除了 &mut 引用,但包括 *const 和 *mut 原始指针)在Rust中都是 Copy ,因此每个只包含基元的结构可以被制作成 Copy . 另一方面,像 Vec 或 Box 这样的结构不是 Copy - 它们故意不实现它 - 因为它们的字节副本将导致双重释放,因为它们的析构函数可以在同一个指针上运行两次 .
&mut
*const
*mut
上面的 Copy 位是我身边的一个小小的题外话,只是为了给出更清晰的画面 . Rust中的所有权基于移动语义 . 当我们说某些 Value 拥有某些东西时,比如“ Box<T> 拥有给定的 T ”,我们指的是它们之间的语义联系,而不是神奇的东西或内置于语言中的东西 . 它只是大多数像 Vec 或 Box 这样的值没有实现 Copy 因此移动而不是复制,它们也(可选)有一个析构函数来清理这些类型可能为它们分配的任何内容(内存,套接字,文件等) . ) .
Box<T>
T
鉴于上述情况,您当然可以编写自己的"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 代表的外国资源 .
Widget
*mut WidgetStruct
2 回答
下面是一个值如何拥有内存并在值被销毁时释放它的另一个示例:
Rust中的tl; dr:"owning"类型不是一些魔术,它们肯定不会硬编码到编译器或语言中 . 它们只是以某种方式编写的类型(不实现
Copy
并且可能具有析构函数)并且具有通过不可复制性和析构函数强制执行的某些语义 .在其核心中,Rust的所有权机制非常简单,并且具有非常简单的规则 .
首先,让我们来定义一下这是什么 . 这很简单 - 当一个值以新名称变为可用并且在旧名称下停止可用时,该名称将被移动:
将值传递给函数时会发生同样的事情:
请注意,这里绝对没有魔法 - 默认情况下,默认情况下,每种类型的每个值的行为与上面的示例相同 . 默认情况下,您或其他人创建的每个结构或枚举的值都将被移动 .
另一个重要的事情是你可以给每个类型一个析构函数,也就是说,当这个类型的值超出范围并被销毁时调用的一段代码 . 例如,与
Vec
或Box
关联的析构函数将释放相应的内存 . 可以通过实现Drop
trait来声明析构函数:有一种方法可以通过实现
Copy
trait来选择退出不可复制性,该特征将该类型标记为可自动复制 - 其值将不再被移动而是被复制:副本以字节方式完成 -
x2
将包含x1
的字节相同副本 .不是每种类型都可以制作
Copy
- 只有那些内部Copy
并且不实现Drop
的那些类型 . 所有原始类型(除了&mut
引用,但包括*const
和*mut
原始指针)在Rust中都是Copy
,因此每个只包含基元的结构可以被制作成Copy
. 另一方面,像Vec
或Box
这样的结构不是Copy
- 它们故意不实现它 - 因为它们的字节副本将导致双重释放,因为它们的析构函数可以在同一个指针上运行两次 .上面的
Copy
位是我身边的一个小小的题外话,只是为了给出更清晰的画面 . Rust中的所有权基于移动语义 . 当我们说某些 Value 拥有某些东西时,比如“Box<T>
拥有给定的T
”,我们指的是它们之间的语义联系,而不是神奇的东西或内置于语言中的东西 . 它只是大多数像Vec
或Box
这样的值没有实现Copy
因此移动而不是复制,它们也(可选)有一个析构函数来清理这些类型可能为它们分配的任何内容(内存,套接字,文件等) . ) .鉴于上述情况,您当然可以编写自己的"owning"类型 . 这是惯用Rust的基石之一,标准库和外部库中的许多代码都是以这种方式编写的 . 例如,某些C API提供了创建和销毁对象的功能 . 在Rust中编写一个"owning"包装器非常容易,它可能非常接近你所要求的:
现在你可以说
Widget
拥有一些由*mut WidgetStruct
代表的外国资源 .