22 个不稳定版本 (7 个重大更改)
| 0.14.1 | 2024 年 8 月 2 日 |
|---|---|
| 0.14.0 | 2024 年 7 月 4 日 |
| 0.14.0-rc.4 | 2024 年 6 月 27 日 |
| 0.13.1 | 2024 年 3 月 18 日 |
| 0.8.0 | 2022 年 7 月 30 日 |
#2305 在 游戏开发
91,481 每月下载量
在 1,166 个包中 使用 (直接使用 7 个)
35KB
297 行
Bevy 指针
在计算机编程中,指针是存储内存地址的对象。它们是构建更复杂数据结构的基本构建块。
它们也是内存安全问题的根本原因:您可以对无效(null)指针进行解引用,在底层内存被释放后访问指针,甚至忽略类型安全并错误地读取或修改底层内存。
Rust 是一种依赖于其类型来强制执行正确性和内存安全的编程语言。因此,Rust 拥有一系列用于处理指针的类型,以及一系列安全和不可安全转换的图表,这使得它们更安全。
bevy_ptr 是一个包,它试图弥合完全不可安全性 *mut () 和安全的 &'a T 之间的差距,使用户能够选择对指针保持哪些不变性,旨在逐步构建更安全的抽象。
如何从头开始构建借用
正确且安全地将指针转换为有效借用是 Rust 中所有 unsafe 代码的核心。查看 [(*const T)::as_ref] 的文档,指针必须满足以下所有条件
- 指针必须正确对齐。
- 指针不能为空,即使是零大小类型。
- 指针必须在有效分配对象的有效范围内(堆栈或堆)。
- 指针必须指向
T的初始化实例。 - 新分配的生存期应有效针对指针指向的值。
- 代码必须遵守Rust的别名规则。在任何给定时刻,对某个值的借用只能是唯一的可变借用或任意数量的不可变借用,并且从
&T转换为&mut T是不被允许的。
请注意,这些规则并非最终版本,并且随着Rust项目确定具体的指针别名规则,这些规则仍在变化,但预期最终的约束集将包含这个列表中的所有规则,而不是子集。
单独满足这个列表已经是非同小可的。幸运的是,Rust核心/标准库提供了一系列指针类型,有助于构建这些安全保证...
标准指针
| 指针类型 | 有生命周期 | 可变 | 强类型 | 对齐 | 非空 | 禁止别名 | 禁止算术操作 |
|---|---|---|---|---|---|---|---|
Box<T> |
所有者 | 是 | 是 | 是 | 是 | 是 | 是 |
&'a mutT |
是 | 是 | 是 | 是 | 是 | 是 | 是 |
&'aT |
是 | 否 | 是 | 是 | 是 | 否 | 是 |
&'a UnsafeCell<T> |
是 | 可能 | 是 | 是 | 是 | 是 | 是 |
NonNull<T> |
否 | 是 | 是 | 否 | 是 | 否 | 否 |
*constT |
否 | 否 | 是 | 否 | 否 | 否 | 否 |
*mutT |
否 | 是 | 是 | 否 | 否 | 否 | 否 |
*const () |
否 | 否 | 否 | 否 | 否 | 否 | 否 |
*mut () |
否 | 是 | 否 | 否 | 否 | 否 | 否 |
&T、&mut T以及Box<T>是Rust开发者最常看到的指针类型。它们是这个列表中唯一完全不需要使用unsafe即可使用的类型。
&UnsafeCell<T>是走向不安全的第一个步骤。UnsafeCell是在语言中从不可变借用获得可变借用的唯一方式,因此它是语言中所有内部可变性的基础原始类型:Cell<T>、RefCell<T>、Mutex<T>、RwLock<T>等都是建立在UnsafeCell<T>之上的。为了将&UnsafeCell<T>安全地转换为&T或&mut T,调用者必须保证所有同时访问都遵循Rust的别名规则。
NonNull<T>与上述类型相比,下降了一个很大的台阶。除了允许别名之外,它是这个列表中第一个放弃借用生命周期和对齐保证的指针类型。它唯一保证的是指针不是空指针,并且它指向类型T的有效实例。如果你曾经使用过C++,那么NonNull<T>与C++引用(T&)非常相似。
*const T和*mut T是大多数有C或C++背景的开发者所认为的指针。
*const () 是这个列表的底部。它们是 Rust 中与 C 的 void* 等效的。请注意,Rust 并没有正式的类型概念来持有任意未类型化的内存地址。指向单元类型(或某些其他零大小类型)只是恰好是惯例。使用它们的唯一合理方式是将它们转换回有类型指针。它们在处理 FFI 以及偶尔需要动态分派但接口过于限制时会出现。一个很好的例子是 RawWaker API,其中单个特质(或一组特质)可能不足以捕获所有使用模式。*mut () 应仅用于携带目标的可变性,因为没有方法可以突变未知类型。
在 Nightly 版本中可用
| 指针类型 | 有生命周期 | 可变 | 强类型 | 对齐 | 非空 | 禁止别名 | 禁止算术操作 |
|---|---|---|---|---|---|---|---|
独特<T> |
所有者 | 是 | 是 | 是 | 是 | 是 | 是 |
共享<T> |
拥有* | 是 | 是 | 是 | 是 | 否 | 是 |
Unique<T> 目前在 nightly Rust 构建中可在 core::ptr 中使用。它是一种指针类型,表现得像它拥有所指向的值。它可以被视为一个不分配初始化时或释放时不会释放的 Box<T>,实际上用于实现常见的类型,如 Box<T>、Vec<T> 等。
Shared<T> 目前在 nightly Rust 构建中可在 core::ptr 中使用。它是支持 Rc<T> 和 Arc<T> 的指针。它的语义允许多个实例共同拥有所指向的数据,因此禁止获取可变借用。
bevy_ptr 目前不支持这些类型,但如果需要,可能会支持这些指针类型的 polyfills。
在 bevy_ptr 中可用
| 指针类型 | 有生命周期 | 可变 | 强类型 | 对齐 | 非空 | 禁止别名 | 禁止算术操作 |
|---|---|---|---|---|---|---|---|
ConstNonNull<T> |
否 | 否 | 是 | 否 | 是 | 否 | 是 |
ThinSlicePtr<'a, T> |
是 | 否 | 是 | 是 | 是 | 是 | 是 |
OwningPtr<'a> |
是 | 是 | 否 | 可能 | 是 | 是 | 否 |
Ptr<'a> |
是 | 否 | 否 | 可能 | 是 | 否 | 否 |
PtrMut<'a> |
是 | 是 | 否 | 可能 | 是 | 是 | 否 |
ConstNonNull<T> 与 NonNull<T> 类似,但禁止将它们安全地转换为允许对所指向的值进行可变访问的类型。它是 *const T 到 NonNull<T> 的 *mut T。
ThinSlicePtr<'a, T> 是一个没有切片长度的 &'a [T]。这意味着它在栈上更小,但是这也意味着本地无法进行边界检查,因此访问切片中的元素是 unsafe。在调试构建中,长度被包含并将进行检查。
OwningPtr<'a>、Ptr<'a> 和 PtrMut<'a> 类似于 NonNull<()>,但尝试恢复 Unique<T>、&T 和 &mut T 的许多安全保证。它们允许在不引入动态分派开销的情况下,以递归的方式将异构类型擦除存储(例如 ECS 表、typemaps)转换为安全的借用。这些类型还支持在类型级别上的可选对齐要求,并在调试构建中验证它。