From 04b3a44359741d2bd5df0550c118f674881cf602 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 9 Mar 2024 12:42:23 -0800 Subject: [PATCH 01/10] Add more comprehensive crate level docs for bevy_ptr --- crates/bevy_ptr/README.md | 100 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ptr/README.md b/crates/bevy_ptr/README.md index c78a6ac544635..bb6c67770d12f 100644 --- a/crates/bevy_ptr/README.md +++ b/crates/bevy_ptr/README.md @@ -6,8 +6,100 @@ [![Docs](https://docs.rs/bevy_ptr/badge.svg)](https://docs.rs/bevy_ptr/latest/bevy_ptr/) [![Discord](https://img.shields.io/discord/691052431525675048.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/bevy) -The `bevy_ptr` crate provides low-level abstractions for working with pointers in a more safe way than using rust's raw pointers. +Pointers in computer programming, according to Wikipedia, are "objects in many programming languages that stores a memory address". +They're a fundamental building block for constructing more complex data structures. -Rust has lifetimed and typed references (`&'a T`), unlifetimed and typed references (`*const T`), but no lifetimed but untyped references. -`bevy_ptr` adds them, called `Ptr<'a>`, `PtrMut<'a>` and `OwningPtr<'a>`. -These types are lifetime-checked so can never lead to problems like use-after-frees and must always point to valid data. +They're also *the* definitive source of memory safety bugs. You can dereference a null pointer. Access a pointer after the underlying +memory has been freed. Ignore type safety and misread or mutate the underlying memory improperly. + +Rust is a programming language that heavily relies on its types to enforce correctness, and by proxy, memory safety. As a result, +Rust has an entire zoo of types for working with pointers, and a graph of safe and unsafe conversions to among them to make working +with them safer. + +`bevy_ptr` is a crate that attempts to bridge the gap between the full blown unsafety of `*mut ()` and the safe `&'a T`, allowing users +to build progressively to choose what invariants to uphold. + +## How to build a Borrow (from scratch) +Correctly and safety converting a pointer into a valid borrow is at the core of all `unsafe` code in Rust. Looking at the documentation for +[`(*const T)::as_ref`], a pointer must satisfy *all* of the following conditions: + + * The pointer must be properly aligned. + * The pointer cannot be null, even for zero sized types. + * The pointer must be within bounds of a valid allocated object (on the stack or the heap). + * The pointer must point to an initialized instance of `T`. + * The newly assigned lifetime should be valid for the value that the pointer is targetting. + * The code must enforce Rust's aliasing rules. Only one mutable borrow or arbitrarily many read-only borrows may exist to a value at any given moment + in time, and converting from `&T` to `&mut T` is always not allowed. + +Note these rules aren't final and are still in flux as the Rust Project hashes out what exactly are the pointer aliasing rules, but the expectation is that the +final set of constraints are going to be a superset of this list, not a subset. + +This list already is non-trivial to satisfy in isolation. Thankfully, the Rust core/standard library provides a progressive list of pointer types that help +build these safety guarentees... + +## Standard Pointers + +|Pointer Type |Lifetime'ed|Mutable|Strongly Typed|Aligned|Not Null|Forbids Aliasing|Forbids Arithmetic| +|:------------------|:----------|:------|:-------------|:------|:-------|:---------------|:-----------------| +|`Box` |Owned |Yes |Yes |Yes |Yes |Yes |Yes | +|`&'a mut T` |Yes |Yes |Yes |Yes |Yes |Yes |Yes | +|`&'a T` |Yes |No |Yes |Yes |Yes |No |Yes | +|`&'a UnsafeCell`|Yes |Maybe |Yes |Yes |Yes |Yes |Yes | +|`NonNull` |No |Yes |Yes |No |Yes |No |No | +|`*const T` |No |No |Yes |No |No |No |No | +|`*mut T` |No |Yes |Yes |No |No |No |No | +|`*const ()` |No |No |No |No |No |No |No | +|`*mut ()` |No |Yes |No |No |No |No |No | + +`&T`, `&mut T`, and `Box` are by far the most common pointer types that Rust developers will see. They're the only ones in this list that are entirely usable +without the use of `unsafe`. + +`&UnsafeCell` is the first step away from safety. `UnsafeCell` is the *only* way to get a mutable borrow from an immutable one in the language, so it's the +base primitive for all interior mutability in the language: `Cell`, `RefCell`, `Mutex`, `RwLock`, etc. are all built on top of +`UnsafeCell`. To safety convert `&UnsafeCell` into a `&T` or `&mut T`, the caller must guarentee that all simultaneous access follow Rust's aliasing rules. + +`NonNull` takes quite a step down from the aforementioned types. In addition to allowing aliasing, it's the first pointer type on this list to drop both +lifetimes and the alignment guarentees of borrows. The only guarentees it provides are that the pointer is not null, and that it points to a valid instance +of type `T`. If you've ever worked with C++, `NonNull` is very close to a C++ reference (`T&`). + +`*const T` and `*mut T` are what most developers with a background in C or C++ would consider pointers. + +`*const ()` is the bottom of this list. They're the Rust equivalent to C's `void*`. Note that Rust doesn't formally have a concept of type that holds an arbitrary +untyped memory address. Pointing at the unit type (or some other zero-sized type) just happens to be the convention here. They show up occasionally when dealing +with FFI and the rare occasion where dynamic dispatch is required, but a trait is too constraining of an interface to work with. A great example of this are the +[RawWaker] APIs, where a singular trait (or set of traits) may be insufficient to capture all usage patterns. `*mut ()`, as far as I know, should never be used. +There is no meaning to being able to mutate a type you don't know about. + +[RawWaker]: https://doc.rust-lang.org/std/task/struct.RawWaker.html + +## Available in Nightly + +|Pointer Type |Lifetime'ed|Mutable|Strongly Typed|Aligned|Not Null|Forbids Aliasing|Forbids Arithmetic| +|:------------------|:----------|:------|:-------------|:------|:-------|:---------------|:-----------------| +|`Unique` |Owned |Yes |Yes |Yes |Yes |Yes |Yes | +|`Shared` |Owned* |Yes |Yes |Yes |Yes |No |Yes | + +`Unique` is currently available in `core::ptr` on nightly Rust builds. It's a pointer type that acts like it owns the value it points to. It can be thought of +as a `Box` that does not allocate on initialization or deallocated when it's dropped, and is in fact used to implement common types like `Box`, `Vec`, +etc. + +`Shared` is currently available in `core::ptr` on nightly Rust builds. It's the pointer that backs both `Rc` and `Arc`. It's semantics allow for +multiple instances to collectively own the data it points to, and as a result, forbids getting a mutable borrow. + +`bevy_ptr` does not support these types right now, but may support polyfills for these pointer types upon request. + +## Available in `bevy_ptr` + +|Pointer Type |Lifetime'ed|Mutable|Strongly Typed|Aligned|Not Null|Forbids Aliasing|Forbids Arithmetic| +|:------------------|:----------|:------|:-------------|:------|:-------|:---------------|:-----------------| +|`ConstNonNull` |No |No |Yes |No |Yes |No |Yes | +|`OwningPtr<'a>` |Yes |Yes |No |Maybe |Yes |Yes |No | +|`Ptr<'a>` |Yes |No |No |Maybe |Yes |No |No | +|`PtrMut<'a>` |Yes |Yes |No |Maybe |Yes |Yes |No | + +`ConstNonNull` is like `NonNull` but disallows safe conversions into types that allow mutable access to the value it points to. It's the `*const T` to +`NonNull`'s `*mut T`. + +`OwningPtr<'a>`, `Ptr<'a>`, and `PtrMut<"a>` act like `NonNull<()>`, but attempts to restore much of the safety guarentees of `Unique`, `&T`, and `&mut T`. +They allow working with heterogenous type erased storage (i.e. ECS tables, typemaps) without the overhead of dynamic dispatch in a manner that progressively +translates back to safe borrows. These types also support optional alignment requirements at a type level, and will verify it on dereference in debug builds. From 363b948ec3ee3dd963996ffd894dff955d2600a9 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 9 Mar 2024 12:52:27 -0800 Subject: [PATCH 02/10] Add the missing ThinSlicePtr --- crates/bevy_ptr/README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ptr/README.md b/crates/bevy_ptr/README.md index bb6c67770d12f..17a05951504ea 100644 --- a/crates/bevy_ptr/README.md +++ b/crates/bevy_ptr/README.md @@ -90,16 +90,20 @@ multiple instances to collectively own the data it points to, and as a result, f ## Available in `bevy_ptr` -|Pointer Type |Lifetime'ed|Mutable|Strongly Typed|Aligned|Not Null|Forbids Aliasing|Forbids Arithmetic| -|:------------------|:----------|:------|:-------------|:------|:-------|:---------------|:-----------------| -|`ConstNonNull` |No |No |Yes |No |Yes |No |Yes | -|`OwningPtr<'a>` |Yes |Yes |No |Maybe |Yes |Yes |No | -|`Ptr<'a>` |Yes |No |No |Maybe |Yes |No |No | -|`PtrMut<'a>` |Yes |Yes |No |Maybe |Yes |Yes |No | +|Pointer Type |Lifetime'ed|Mutable|Strongly Typed|Aligned|Not Null|Forbids Aliasing|Forbids Arithmetic| +|:--------------------|:----------|:------|:-------------|:------|:-------|:---------------|:-----------------| +|`ConstNonNull` |No |No |Yes |No |Yes |No |Yes | +|`ThinSlicePtr<'a, T>`|Yes |No |Yes |Yes |Yes |Yes |Yes | +|`OwningPtr<'a>` |Yes |Yes |No |Maybe |Yes |Yes |No | +|`Ptr<'a>` |Yes |No |No |Maybe |Yes |No |No | +|`PtrMut<'a>` |Yes |Yes |No |Maybe |Yes |Yes |No | `ConstNonNull` is like `NonNull` but disallows safe conversions into types that allow mutable access to the value it points to. It's the `*const T` to `NonNull`'s `*mut T`. +`ThinSlicePtr<'a, T>` is a `&'a [T]` without the slice length. This means it's smaller on the stack, but it means bounds checking is impossible locally, so +accessing elements in the slice is `unsafe`. In debug builds, the length is included and will be checked. + `OwningPtr<'a>`, `Ptr<'a>`, and `PtrMut<"a>` act like `NonNull<()>`, but attempts to restore much of the safety guarentees of `Unique`, `&T`, and `&mut T`. They allow working with heterogenous type erased storage (i.e. ECS tables, typemaps) without the overhead of dynamic dispatch in a manner that progressively translates back to safe borrows. These types also support optional alignment requirements at a type level, and will verify it on dereference in debug builds. From 4096571eb5dd262dc38818cd5ae337650fdfcaec Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 9 Mar 2024 12:54:37 -0800 Subject: [PATCH 03/10] Fix typos --- crates/bevy_ptr/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ptr/README.md b/crates/bevy_ptr/README.md index 17a05951504ea..0d74133002e13 100644 --- a/crates/bevy_ptr/README.md +++ b/crates/bevy_ptr/README.md @@ -35,7 +35,7 @@ Note these rules aren't final and are still in flux as the Rust Project hashes o final set of constraints are going to be a superset of this list, not a subset. This list already is non-trivial to satisfy in isolation. Thankfully, the Rust core/standard library provides a progressive list of pointer types that help -build these safety guarentees... +build these safety guarantees... ## Standard Pointers @@ -56,10 +56,10 @@ without the use of `unsafe`. `&UnsafeCell` is the first step away from safety. `UnsafeCell` is the *only* way to get a mutable borrow from an immutable one in the language, so it's the base primitive for all interior mutability in the language: `Cell`, `RefCell`, `Mutex`, `RwLock`, etc. are all built on top of -`UnsafeCell`. To safety convert `&UnsafeCell` into a `&T` or `&mut T`, the caller must guarentee that all simultaneous access follow Rust's aliasing rules. +`UnsafeCell`. To safety convert `&UnsafeCell` into a `&T` or `&mut T`, the caller must guarantee that all simultaneous access follow Rust's aliasing rules. `NonNull` takes quite a step down from the aforementioned types. In addition to allowing aliasing, it's the first pointer type on this list to drop both -lifetimes and the alignment guarentees of borrows. The only guarentees it provides are that the pointer is not null, and that it points to a valid instance +lifetimes and the alignment guarantees of borrows. The only guarantees it provides are that the pointer is not null, and that it points to a valid instance of type `T`. If you've ever worked with C++, `NonNull` is very close to a C++ reference (`T&`). `*const T` and `*mut T` are what most developers with a background in C or C++ would consider pointers. @@ -104,6 +104,6 @@ multiple instances to collectively own the data it points to, and as a result, f `ThinSlicePtr<'a, T>` is a `&'a [T]` without the slice length. This means it's smaller on the stack, but it means bounds checking is impossible locally, so accessing elements in the slice is `unsafe`. In debug builds, the length is included and will be checked. -`OwningPtr<'a>`, `Ptr<'a>`, and `PtrMut<"a>` act like `NonNull<()>`, but attempts to restore much of the safety guarentees of `Unique`, `&T`, and `&mut T`. +`OwningPtr<'a>`, `Ptr<'a>`, and `PtrMut<"a>` act like `NonNull<()>`, but attempts to restore much of the safety guarantees of `Unique`, `&T`, and `&mut T`. They allow working with heterogenous type erased storage (i.e. ECS tables, typemaps) without the overhead of dynamic dispatch in a manner that progressively translates back to safe borrows. These types also support optional alignment requirements at a type level, and will verify it on dereference in debug builds. From e94e2e08e240adcc41ec80b0fac2bdeb0caae4ae Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 9 Mar 2024 13:00:02 -0800 Subject: [PATCH 04/10] More typos --- crates/bevy_ptr/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ptr/README.md b/crates/bevy_ptr/README.md index 0d74133002e13..fb547360d70f3 100644 --- a/crates/bevy_ptr/README.md +++ b/crates/bevy_ptr/README.md @@ -27,7 +27,7 @@ Correctly and safety converting a pointer into a valid borrow is at the core of * The pointer cannot be null, even for zero sized types. * The pointer must be within bounds of a valid allocated object (on the stack or the heap). * The pointer must point to an initialized instance of `T`. - * The newly assigned lifetime should be valid for the value that the pointer is targetting. + * The newly assigned lifetime should be valid for the value that the pointer is targeting. * The code must enforce Rust's aliasing rules. Only one mutable borrow or arbitrarily many read-only borrows may exist to a value at any given moment in time, and converting from `&T` to `&mut T` is always not allowed. From c89305cc4963b7b52ebb0a0a40088d2cf1382ea3 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 9 Mar 2024 14:06:33 -0800 Subject: [PATCH 05/10] Change first person --- crates/bevy_ptr/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ptr/README.md b/crates/bevy_ptr/README.md index fb547360d70f3..b102bb10d51bd 100644 --- a/crates/bevy_ptr/README.md +++ b/crates/bevy_ptr/README.md @@ -65,10 +65,10 @@ of type `T`. If you've ever worked with C++, `NonNull` is very close to a C++ `*const T` and `*mut T` are what most developers with a background in C or C++ would consider pointers. `*const ()` is the bottom of this list. They're the Rust equivalent to C's `void*`. Note that Rust doesn't formally have a concept of type that holds an arbitrary -untyped memory address. Pointing at the unit type (or some other zero-sized type) just happens to be the convention here. They show up occasionally when dealing -with FFI and the rare occasion where dynamic dispatch is required, but a trait is too constraining of an interface to work with. A great example of this are the -[RawWaker] APIs, where a singular trait (or set of traits) may be insufficient to capture all usage patterns. `*mut ()`, as far as I know, should never be used. -There is no meaning to being able to mutate a type you don't know about. +untyped memory address. Pointing at the unit type (or some other zero-sized type) just happens to be the convention. The only way to reasonably use them is to +cast back to a typed pointer. They show up occasionally when dealing with FFI and the rare occasion where dynamic dispatch is required, but a trait is too +constraining of an interface to work with. A great example of this are the [RawWaker] APIs, where a singular trait (or set of traits) may be insufficient to capture +all usage patterns. `*mut ()` should only be used to carry the mutability of the target, and as there is no way to to mutate an unknown type. [RawWaker]: https://doc.rust-lang.org/std/task/struct.RawWaker.html From 46c0153264cd1a69323005c5e1827aa05814adbf Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 9 Mar 2024 18:29:59 -0800 Subject: [PATCH 06/10] Link the wikipedia polyfill page --- crates/bevy_ptr/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ptr/README.md b/crates/bevy_ptr/README.md index b102bb10d51bd..543ca91efa690 100644 --- a/crates/bevy_ptr/README.md +++ b/crates/bevy_ptr/README.md @@ -86,7 +86,9 @@ etc. `Shared` is currently available in `core::ptr` on nightly Rust builds. It's the pointer that backs both `Rc` and `Arc`. It's semantics allow for multiple instances to collectively own the data it points to, and as a result, forbids getting a mutable borrow. -`bevy_ptr` does not support these types right now, but may support polyfills for these pointer types upon request. +`bevy_ptr` does not support these types right now, but may support [polyfills] for these pointer types upon request. + +[polyfills]: https://en.wikipedia.org/wiki/Polyfill_(programming) ## Available in `bevy_ptr` From 540d010718d2651cc514c20cd2c6411a23b13458 Mon Sep 17 00:00:00 2001 From: James Liu Date: Sat, 9 Mar 2024 23:02:34 -0800 Subject: [PATCH 07/10] Apply suggestions from code review Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com> --- crates/bevy_ptr/README.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/bevy_ptr/README.md b/crates/bevy_ptr/README.md index 543ca91efa690..f3285ae22fcc5 100644 --- a/crates/bevy_ptr/README.md +++ b/crates/bevy_ptr/README.md @@ -9,17 +9,16 @@ Pointers in computer programming, according to Wikipedia, are "objects in many programming languages that stores a memory address". They're a fundamental building block for constructing more complex data structures. -They're also *the* definitive source of memory safety bugs. You can dereference a null pointer. Access a pointer after the underlying -memory has been freed. Ignore type safety and misread or mutate the underlying memory improperly. +They're also *the* definitive source of memory safety bugs: you can dereference a invalid (null) pointer, access a pointer after the underlying +memory has been freed, and even ignore type safety and misread or mutate the underlying memory improperly. Rust is a programming language that heavily relies on its types to enforce correctness, and by proxy, memory safety. As a result, -Rust has an entire zoo of types for working with pointers, and a graph of safe and unsafe conversions to among them to make working -with them safer. +Rust has an entire zoo of types for working with pointers, and a graph of safe and unsafe conversions that make working with them safer. `bevy_ptr` is a crate that attempts to bridge the gap between the full blown unsafety of `*mut ()` and the safe `&'a T`, allowing users to build progressively to choose what invariants to uphold. -## How to build a Borrow (from scratch) +## How to Build a Borrow (From Scratch) Correctly and safety converting a pointer into a valid borrow is at the core of all `unsafe` code in Rust. Looking at the documentation for [`(*const T)::as_ref`], a pointer must satisfy *all* of the following conditions: @@ -29,7 +28,7 @@ Correctly and safety converting a pointer into a valid borrow is at the core of * The pointer must point to an initialized instance of `T`. * The newly assigned lifetime should be valid for the value that the pointer is targeting. * The code must enforce Rust's aliasing rules. Only one mutable borrow or arbitrarily many read-only borrows may exist to a value at any given moment - in time, and converting from `&T` to `&mut T` is always not allowed. + in time, and converting from `&T` to `&mut T` is never allowed. Note these rules aren't final and are still in flux as the Rust Project hashes out what exactly are the pointer aliasing rules, but the expectation is that the final set of constraints are going to be a superset of this list, not a subset. @@ -59,7 +58,7 @@ base primitive for all interior mutability in the language: `Cell`, `RefCell< `UnsafeCell`. To safety convert `&UnsafeCell` into a `&T` or `&mut T`, the caller must guarantee that all simultaneous access follow Rust's aliasing rules. `NonNull` takes quite a step down from the aforementioned types. In addition to allowing aliasing, it's the first pointer type on this list to drop both -lifetimes and the alignment guarantees of borrows. The only guarantees it provides are that the pointer is not null, and that it points to a valid instance +lifetimes and the alignment guarantees of borrows. Its only guarantees are that the pointer is not null and that it points to a valid instance of type `T`. If you've ever worked with C++, `NonNull` is very close to a C++ reference (`T&`). `*const T` and `*mut T` are what most developers with a background in C or C++ would consider pointers. @@ -86,7 +85,7 @@ etc. `Shared` is currently available in `core::ptr` on nightly Rust builds. It's the pointer that backs both `Rc` and `Arc`. It's semantics allow for multiple instances to collectively own the data it points to, and as a result, forbids getting a mutable borrow. -`bevy_ptr` does not support these types right now, but may support [polyfills] for these pointer types upon request. +`bevy_ptr` does not support these types right now, but may support [polyfills] for these pointer types if the need arises. [polyfills]: https://en.wikipedia.org/wiki/Polyfill_(programming) @@ -106,6 +105,6 @@ multiple instances to collectively own the data it points to, and as a result, f `ThinSlicePtr<'a, T>` is a `&'a [T]` without the slice length. This means it's smaller on the stack, but it means bounds checking is impossible locally, so accessing elements in the slice is `unsafe`. In debug builds, the length is included and will be checked. -`OwningPtr<'a>`, `Ptr<'a>`, and `PtrMut<"a>` act like `NonNull<()>`, but attempts to restore much of the safety guarantees of `Unique`, `&T`, and `&mut T`. +`OwningPtr<'a>`, `Ptr<'a>`, and `PtrMut<'a>` act like `NonNull<()>`, but attempts to restore much of the safety guarantees of `Unique`, `&T`, and `&mut T`. They allow working with heterogenous type erased storage (i.e. ECS tables, typemaps) without the overhead of dynamic dispatch in a manner that progressively translates back to safe borrows. These types also support optional alignment requirements at a type level, and will verify it on dereference in debug builds. From 55d0fa7c42fb92b141dc07738e0d3ece714af094 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 9 Mar 2024 23:06:49 -0800 Subject: [PATCH 08/10] Rewording the summary. --- crates/bevy_ptr/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ptr/README.md b/crates/bevy_ptr/README.md index f3285ae22fcc5..cab00e0e890b9 100644 --- a/crates/bevy_ptr/README.md +++ b/crates/bevy_ptr/README.md @@ -6,8 +6,8 @@ [![Docs](https://docs.rs/bevy_ptr/badge.svg)](https://docs.rs/bevy_ptr/latest/bevy_ptr/) [![Discord](https://img.shields.io/discord/691052431525675048.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/bevy) -Pointers in computer programming, according to Wikipedia, are "objects in many programming languages that stores a memory address". -They're a fundamental building block for constructing more complex data structures. +Pointers in computer programming are objects that store a memory address. They're a fundamental building block for constructing more +complex data structures. They're also *the* definitive source of memory safety bugs: you can dereference a invalid (null) pointer, access a pointer after the underlying memory has been freed, and even ignore type safety and misread or mutate the underlying memory improperly. @@ -16,7 +16,7 @@ Rust is a programming language that heavily relies on its types to enforce corre Rust has an entire zoo of types for working with pointers, and a graph of safe and unsafe conversions that make working with them safer. `bevy_ptr` is a crate that attempts to bridge the gap between the full blown unsafety of `*mut ()` and the safe `&'a T`, allowing users -to build progressively to choose what invariants to uphold. +to choose what invariants to uphold for their pointer, with the intent to enable building progressively safer abstractions. ## How to Build a Borrow (From Scratch) Correctly and safety converting a pointer into a valid borrow is at the core of all `unsafe` code in Rust. Looking at the documentation for From 4001c6bc29360b7f2821aff67cceb927fa022fee Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 9 Mar 2024 23:09:01 -0800 Subject: [PATCH 09/10] No explicit alignemnt --- crates/bevy_ptr/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ptr/README.md b/crates/bevy_ptr/README.md index cab00e0e890b9..ac99d006462e7 100644 --- a/crates/bevy_ptr/README.md +++ b/crates/bevy_ptr/README.md @@ -39,7 +39,7 @@ build these safety guarantees... ## Standard Pointers |Pointer Type |Lifetime'ed|Mutable|Strongly Typed|Aligned|Not Null|Forbids Aliasing|Forbids Arithmetic| -|:------------------|:----------|:------|:-------------|:------|:-------|:---------------|:-----------------| +|-------------------|-----------|-------|--------------|-------|--------|----------------|------------------| |`Box` |Owned |Yes |Yes |Yes |Yes |Yes |Yes | |`&'a mut T` |Yes |Yes |Yes |Yes |Yes |Yes |Yes | |`&'a T` |Yes |No |Yes |Yes |Yes |No |Yes | @@ -74,7 +74,7 @@ all usage patterns. `*mut ()` should only be used to carry the mutability of the ## Available in Nightly |Pointer Type |Lifetime'ed|Mutable|Strongly Typed|Aligned|Not Null|Forbids Aliasing|Forbids Arithmetic| -|:------------------|:----------|:------|:-------------|:------|:-------|:---------------|:-----------------| +|-------------------|-----------|-------|--------------|-------|--------|----------------|------------------| |`Unique` |Owned |Yes |Yes |Yes |Yes |Yes |Yes | |`Shared` |Owned* |Yes |Yes |Yes |Yes |No |Yes | @@ -92,7 +92,7 @@ multiple instances to collectively own the data it points to, and as a result, f ## Available in `bevy_ptr` |Pointer Type |Lifetime'ed|Mutable|Strongly Typed|Aligned|Not Null|Forbids Aliasing|Forbids Arithmetic| -|:--------------------|:----------|:------|:-------------|:------|:-------|:---------------|:-----------------| +|---------------------|-----------|-------|--------------|-------|--------|----------------|------------------| |`ConstNonNull` |No |No |Yes |No |Yes |No |Yes | |`ThinSlicePtr<'a, T>`|Yes |No |Yes |Yes |Yes |Yes |Yes | |`OwningPtr<'a>` |Yes |Yes |No |Maybe |Yes |Yes |No | From fcb22bb416b45d729575e643105a7e70616af27b Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 11 Mar 2024 23:29:55 -0700 Subject: [PATCH 10/10] Markdown Formatting --- crates/bevy_ptr/README.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/crates/bevy_ptr/README.md b/crates/bevy_ptr/README.md index ac99d006462e7..5d551a1a0e190 100644 --- a/crates/bevy_ptr/README.md +++ b/crates/bevy_ptr/README.md @@ -6,29 +6,30 @@ [![Docs](https://docs.rs/bevy_ptr/badge.svg)](https://docs.rs/bevy_ptr/latest/bevy_ptr/) [![Discord](https://img.shields.io/discord/691052431525675048.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/bevy) -Pointers in computer programming are objects that store a memory address. They're a fundamental building block for constructing more +Pointers in computer programming are objects that store a memory address. They're a fundamental building block for constructing more complex data structures. -They're also *the* definitive source of memory safety bugs: you can dereference a invalid (null) pointer, access a pointer after the underlying +They're also *the* definitive source of memory safety bugs: you can dereference a invalid (null) pointer, access a pointer after the underlying memory has been freed, and even ignore type safety and misread or mutate the underlying memory improperly. -Rust is a programming language that heavily relies on its types to enforce correctness, and by proxy, memory safety. As a result, +Rust is a programming language that heavily relies on its types to enforce correctness, and by proxy, memory safety. As a result, Rust has an entire zoo of types for working with pointers, and a graph of safe and unsafe conversions that make working with them safer. -`bevy_ptr` is a crate that attempts to bridge the gap between the full blown unsafety of `*mut ()` and the safe `&'a T`, allowing users +`bevy_ptr` is a crate that attempts to bridge the gap between the full blown unsafety of `*mut ()` and the safe `&'a T`, allowing users to choose what invariants to uphold for their pointer, with the intent to enable building progressively safer abstractions. ## How to Build a Borrow (From Scratch) + Correctly and safety converting a pointer into a valid borrow is at the core of all `unsafe` code in Rust. Looking at the documentation for [`(*const T)::as_ref`], a pointer must satisfy *all* of the following conditions: - * The pointer must be properly aligned. - * The pointer cannot be null, even for zero sized types. - * The pointer must be within bounds of a valid allocated object (on the stack or the heap). - * The pointer must point to an initialized instance of `T`. - * The newly assigned lifetime should be valid for the value that the pointer is targeting. - * The code must enforce Rust's aliasing rules. Only one mutable borrow or arbitrarily many read-only borrows may exist to a value at any given moment - in time, and converting from `&T` to `&mut T` is never allowed. +* The pointer must be properly aligned. +* The pointer cannot be null, even for zero sized types. +* The pointer must be within bounds of a valid allocated object (on the stack or the heap). +* The pointer must point to an initialized instance of `T`. +* The newly assigned lifetime should be valid for the value that the pointer is targeting. +* The code must enforce Rust's aliasing rules. Only one mutable borrow or arbitrarily many read-only borrows may exist to a value at any given moment + in time, and converting from `&T` to `&mut T` is never allowed. Note these rules aren't final and are still in flux as the Rust Project hashes out what exactly are the pointer aliasing rules, but the expectation is that the final set of constraints are going to be a superset of this list, not a subset. @@ -65,8 +66,8 @@ of type `T`. If you've ever worked with C++, `NonNull` is very close to a C++ `*const ()` is the bottom of this list. They're the Rust equivalent to C's `void*`. Note that Rust doesn't formally have a concept of type that holds an arbitrary untyped memory address. Pointing at the unit type (or some other zero-sized type) just happens to be the convention. The only way to reasonably use them is to -cast back to a typed pointer. They show up occasionally when dealing with FFI and the rare occasion where dynamic dispatch is required, but a trait is too -constraining of an interface to work with. A great example of this are the [RawWaker] APIs, where a singular trait (or set of traits) may be insufficient to capture +cast back to a typed pointer. They show up occasionally when dealing with FFI and the rare occasion where dynamic dispatch is required, but a trait is too +constraining of an interface to work with. A great example of this are the [RawWaker] APIs, where a singular trait (or set of traits) may be insufficient to capture all usage patterns. `*mut ()` should only be used to carry the mutability of the target, and as there is no way to to mutate an unknown type. [RawWaker]: https://doc.rust-lang.org/std/task/struct.RawWaker.html