Pointers Rust如何知道哪些类型拥有资源?
当有一个指向某个堆分配内存的框指针时,我假设Rust具有所有权的“硬编码”知识,因此当通过调用某个函数来转移所有权时,资源会被移动,函数中的参数就是新的所有者 然而,例如,向量是如何发生这种情况的?它们也“拥有”自己的资源,所有权机制也适用于框指针——但它们是存储在变量本身中的常规值,而不是指针。Rust(知道)如何在这种情况下应用所有权机制 我可以自己制作拥有资源的类型吗?tl;dr:Rust中的“拥有”类型并不是什么神奇的东西,它们肯定不是硬编码到编译器或语言中的。它们只是以某种方式编写的类型(不实现Pointers Rust如何知道哪些类型拥有资源?,pointers,heap,rust,ownership,Pointers,Heap,Rust,Ownership,当有一个指向某个堆分配内存的框指针时,我假设Rust具有所有权的“硬编码”知识,因此当通过调用某个函数来转移所有权时,资源会被移动,函数中的参数就是新的所有者 然而,例如,向量是如何发生这种情况的?它们也“拥有”自己的资源,所有权机制也适用于框指针——但它们是存储在变量本身中的常规值,而不是指针。Rust(知道)如何在这种情况下应用所有权机制 我可以自己制作拥有资源的类型吗?tl;dr:Rust中的“拥有”类型并不是什么神奇的东西,它们肯定不是硬编码到编译器或语言中的。它们只是以某种方式编写的类
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来声明析构函数:
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
相同的字节副本
并非每种类型都可以进行复制
——只有那些具有复制
内部且未实现拖放的类型。所有基元类型(除了&mut
引用,但包括*const
和*mut
原始指针)都是Copy
生锈的,因此每个只包含基元的结构都可以制作成Copy
。另一方面,像Vec
或Box
这样的结构不是Copy
——它们故意不实现它,因为它们的字节复制将导致双重释放,因为它们的析构函数可以在同一指针上运行两次
上面的Copy
一点在我这边是有点离题的,只是为了让画面更清晰。Rust中的所有权基于移动语义。当我们说某些值拥有某些东西时,比如“Box
拥有给定的T
”,我们指的是它们之间的语义联系,而不是神奇的东西或语言中固有的东西。只是像Vec
或Box
这样的大多数值都没有实现Copy
,因此被移动而不是复制,而且它们还(可选)有一个析构函数,可以清除这些类型可能分配给它们的任何内容(内存、套接字、文件等)
鉴于上述情况,您当然可以编写自己的“拥有”类型。这是惯用Rust的基石之一,标准库和外部库中的许多代码都是以这种方式编写的。例如,一些C API提供了创建和销毁对象的函数。在它们周围写一个“拥有”的包装很容易生锈,而且可能非常接近你的要求:
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) }
}
}
现在您可以说,小部件
拥有一些由*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();
}
我不确定我是否理解你的问题,但是当你把一个值放到一个向量中时,这个值就属于这个向量了。我想如果你能提供一个你所问问题的代码示例会很有帮助。我现在没有时间写完整的答案,我只想说,Box
不是特殊的或硬编码的。(嗯,目前有些方面是,但这些方面都与这个问题无关,它们只是硬编码的,因为用纯库代码表达这些内容的语言功能还没有完成。)Box的所有权与Vec的所有权完全相同。@Adrian但是当你把一个值放入一个向量中时,这个值就被向量所拥有。“AFAIK价值观不是所有的,而是资源。我不是在问向量中的数据,我是在问向量变量拥有内存这一事实,就像盒子一样——但它不是盒子。我基本上只是想问一下Rust的内部结构,即所有权适用于哪些结构,以及如何确定它。@delnan我认为所有权必须在语言中“烘焙”出来?如果您没有时间解释这一点,您是否有这样的链接?当您创建一个新向量(Vec::new
)或推送到一个向量时,内存由该向量分配,例如在上Vec
实现了Drop
,这使得在上发生的向量销毁时可以释放内存。请注意,基元类型集还包括原始指针*mut
和*const
,它们用于实现Box
和Vec
以及其他容器类型。如果不使用下拉菜单
impl,框
和Vec
可能完全是Copy
——这将是不安全的
,语义错误。因为它经常会让人绊倒,请注意,移动和复制都是id