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
请注意,这里 绝对没有魔法 - 只是默认情况下 every 值 every type 的行为与上面的例子一样。您或其他人默认创建的每个结构或枚举的值将被移动。
另外一个重要的事情是,你可以给每个类型一个析构函数,也就是当这个类型的值超出范围被销毁时调用的一段代码.例如,与 Vec
或 Box
关联的析构函数将释放相应的内存块。可以通过实现 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
.另一方面,像 Vec
或 Box
这样的结构不是 Copy
- 它们故意不实现它,因为按字节复制它们会导致双重释放,因为它们的析构函数可以是 运行 在同一个指针上两次。
上面的Copy
位是我这边的一点题外话,只是为了给出一个更清晰的画面。 Rust 中的所有权基于移动语义。当我们说某些值拥有某些东西时,比如“Box<T>
拥有给定的 T
”,我们指的是它们之间的 语义 连接,而不是某种神奇的东西或某些东西内置于语言中。只是大多数这样的值,如 Vec
或 Box
没有实现 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();
}
当一个盒子指向某个堆分配的内存时,我假设 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
请注意,这里 绝对没有魔法 - 只是默认情况下 every 值 every type 的行为与上面的例子一样。您或其他人默认创建的每个结构或枚举的值将被移动。
另外一个重要的事情是,你可以给每个类型一个析构函数,也就是当这个类型的值超出范围被销毁时调用的一段代码.例如,与 Vec
或 Box
关联的析构函数将释放相应的内存块。可以通过实现 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
.另一方面,像 Vec
或 Box
这样的结构不是 Copy
- 它们故意不实现它,因为按字节复制它们会导致双重释放,因为它们的析构函数可以是 运行 在同一个指针上两次。
上面的Copy
位是我这边的一点题外话,只是为了给出一个更清晰的画面。 Rust 中的所有权基于移动语义。当我们说某些值拥有某些东西时,比如“Box<T>
拥有给定的 T
”,我们指的是它们之间的 语义 连接,而不是某种神奇的东西或某些东西内置于语言中。只是大多数这样的值,如 Vec
或 Box
没有实现 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();
}