#pointers #bevy #memory-safety #no-std #safe #untyped #type

no-std bevy_ptr

用于以更安全的方式处理无类型指针的实用工具

22 个不稳定版本 (7 个重大更改)

0.14.1 2024 年 8 月 2 日
0.14.0 2024 年 7 月 4 日
0.14.0-rc.42024 年 6 月 27 日
0.13.1 2024 年 3 月 18 日
0.8.0 2022 年 7 月 30 日

#2305游戏开发

Download history 11546/week @ 2024-05-05 11254/week @ 2024-05-12 10660/week @ 2024-05-19 12448/week @ 2024-05-26 13683/week @ 2024-06-02 10289/week @ 2024-06-09 12880/week @ 2024-06-16 12569/week @ 2024-06-23 15911/week @ 2024-06-30 18644/week @ 2024-07-07 20409/week @ 2024-07-14 21692/week @ 2024-07-21 23634/week @ 2024-07-28 19576/week @ 2024-08-04 26244/week @ 2024-08-11 19838/week @ 2024-08-18

91,481 每月下载量
1,166 个包中 使用 (直接使用 7 个)

MIT/Apache

35KB
297

Bevy 指针

License Crates.io Downloads Docs Discord

在计算机编程中,指针是存储内存地址的对象。它们是构建更复杂数据结构的基本构建块。

它们也是内存安全问题的根本原因:您可以对无效(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 TNonNull<T>*mut T

ThinSlicePtr<'a, T> 是一个没有切片长度的 &'a [T]。这意味着它在栈上更小,但是这也意味着本地无法进行边界检查,因此访问切片中的元素是 unsafe。在调试构建中,长度被包含并将进行检查。

OwningPtr<'a>Ptr<'a>PtrMut<'a> 类似于 NonNull<()>,但尝试恢复 Unique<T>&T&mut T 的许多安全保证。它们允许在不引入动态分派开销的情况下,以递归的方式将异构类型擦除存储(例如 ECS 表、typemaps)转换为安全的借用。这些类型还支持在类型级别上的可选对齐要求,并在调试构建中验证它。

无运行时依赖