diff options
author | SoniEx2 <endermoneymod@gmail.com> | 2023-09-05 11:39:52 -0300 |
---|---|---|
committer | SoniEx2 <endermoneymod@gmail.com> | 2023-09-05 11:39:52 -0300 |
commit | b49e8e6ca2185396b8cd05d0b4b83b3ae6b80dae (patch) | |
tree | 0d4a53f42c710668ec265be4967cfb20a5919caf | |
parent | dbc79bbbc30308a86788b9b3d49b8f4c3b39b30a (diff) |
Update crate documentation
Hopefully this clarifies things
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/lib.rs | 128 | ||||
-rw-r--r-- | src/srce.rs | 16 | ||||
-rw-r--r-- | tests/ui/no_uaf_2.rs | 5 | ||||
-rw-r--r-- | tests/ui/no_uaf_2.stderr | 8 |
5 files changed, 68 insertions, 91 deletions
diff --git a/Cargo.toml b/Cargo.toml index 75d049c..7eb9f7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "selfref" -version = "0.4.2" +version = "0.4.3" authors = ["SoniEx2 <endermoneymod@gmail.com>"] edition = "2021" description = "Semi-pain-free self-referential pinned types" diff --git a/src/lib.rs b/src/lib.rs index 17c4c10..bc06faf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,65 +8,53 @@ #![no_std] #![cfg_attr(feature="nightly", feature(dropck_eyepatch))] -//! Pain-free self-referential/recursively-referential pinned types. +//! An experimental approach to self-referential structs in Rust. //! -//! Because this crate optimizes for recursive references, it's much nicer to -//! use for porting/copying C code than alternatives. However, the cost is that -//! it requires generic associated types (GATs). Further, this is the only -//! crate for self-referential types which supports additional lifetimes than -//! the self-referential one (with the small ergonomic caveat that closures -//! don't support where bounds for now, so a proper trait has to be used). +//! This crate provides an alternative approach to self-referential structs, +//! where instead of providing you with a macro or framework where you define +//! a self-referential struct and it handles all of the details for you, we try +//! to expose the abstractions and building blocks for making self-referential +//! structs work well in safe Rust. //! -//! When using this crate, all you have to do is define your struct like you -//! would in C, and then tell `selfref` about it. However, do watch out; the -//! following will not be usable, despite compiling: +//! For example, a [`Holder`] is a safe wrapper around a self-referential +//! struct, providing safe APIs for constructing and manipulating a +//! self-referential struct. However, and unlike other self-referential crates, +//! it does not dictate the backing storage of the struct. The [`Opaque`] trait +//! is used to identify a self-referential struct for use with a [`Holder`] - +//! since Rust does not support higher kinded types (HKTs), this crate uses +//! generic associated types (GATs) as a workaround. //! -//! ```rust -//! use selfref::opaque; -//! -//! // This is the actual self-referential struct. -//! struct MySelfRefStruct<'this> { -//! this: &'this MySelfRefStruct<'this>, -//! } -//! -//! // This is a "marker" type, used to tell `selfref` about the type. -//! // This is actually the "type family" GAT pattern. -//! struct MySelfRefStructKey; -//! -//! // This tells `selfref` about our type. -//! opaque! { -//! impl Opaque for MySelfRefStructKey { -//! type Kind<'this> = MySelfRefStruct<'this>; -//! } -//! } -//! ``` +//! To use the crate, first define a self-referential struct in plain Rust: //! -//! That's because there's no way to create the reference -//! `&'this MySelfRefStruct<'this>` before constructing the struct! That is, -//! you cannot do this: +//! ```rust +//! use std::cell::Cell; //! -//! ```rust compile_fail +//! // Your self-referential struct. //! struct MySelfRefStruct<'this> { -//! this: &'this MySelfRefStruct<'this>, -//! } -//! -//! fn main() { -//! let x = MySelfRefStruct { this: &x }; +//! // Rust uses RAII-like struct construction, as a result this must be +//! // somehow initialized after the struct. We can use an Option in a Cell +//! // for this. +//! this: Cell<Option<&'this MySelfRefStruct<'this>>>, //! } //! ``` //! -//! But then, you cannot do this in C either. Instead, to make it usable, we -//! use an `Option` and, to be able to set it, a `Cell`, like so: +//! Then, define a type to implement the `Opaque`. This can be done +//! automatically with the `opaque` macro: //! //! ```rust -//! use std::cell::Cell; +//! # use std::cell::Cell; +//! # // Your self-referential struct. +//! # struct MySelfRefStruct<'this> { +//! # // Rust uses RAII-like struct construction, as a result this must be +//! # // somehow initialized after the struct. We can use an Option in a Cell +//! # // for this. +//! # this: Cell<Option<&'this MySelfRefStruct<'this>>>, +//! # } //! //! use selfref::opaque; //! -//! struct MySelfRefStruct<'this> { -//! this: Cell<Option<&'this MySelfRefStruct<'this>>>, -//! } -//! +//! // A "marker type" that implements `Opaque`. +//! // This follows the "type family" GAT pattern. //! struct MySelfRefStructKey; //! //! opaque! { @@ -74,29 +62,38 @@ //! type Kind<'this> = MySelfRefStruct<'this>; //! } //! } +//! +//! // Alternatively, it is possible to implement `Opaque` on, for example, +//! // `MySelfRefStruct<'static>`, but the added lifetime adds verbosity which +//! // may be considered unnecessary/undesired. //! ``` //! -//! and then we can use it: +//! Now you can construct a `Holder` and pick a storage for it. For example, +//! in a `Box`: //! //! ```rust -//! // lines from the above example have been omitted -//! //! # use std::cell::Cell; -//! # -//! use selfref::Holder; -//! # use selfref::opaque; -//! # +//! # // Your self-referential struct. //! # struct MySelfRefStruct<'this> { +//! # // Rust uses RAII-like struct construction, as a result this must be +//! # // somehow initialized after the struct. We can use an Option in a Cell +//! # // for this. //! # this: Cell<Option<&'this MySelfRefStruct<'this>>>, //! # } -//! # +//! # use selfref::opaque; +//! # // A "marker type" that implements `Opaque`. +//! # // This follows the "type family" GAT pattern. //! # struct MySelfRefStructKey; -//! # //! # opaque! { //! # impl Opaque for MySelfRefStructKey { //! # type Kind<'this> = MySelfRefStruct<'this>; //! # } //! # } +//! # // Alternatively, it is possible to implement `Opaque` on, for example, +//! # // `MySelfRefStruct<'static>`, but the added lifetime adds verbosity which +//! # // may be considered unnecessary/undesired. +//! +//! use selfref::Holder; //! //! fn main() { //! // first, construct the struct @@ -122,12 +119,9 @@ //! This is a more complex example borrowing from an external lifetime: //! //! ```rust -//! #![feature(pin_macro)] -//! //! use core::cell::Cell; //! use core::marker::PhantomData; //! use core::pin::Pin; -//! use core::pin::pin; //! //! use selfref::Holder; //! use selfref::opaque; @@ -150,7 +144,7 @@ //! let stack_str = core::str::from_utf8(&stack_array).unwrap(); //! //! // construct the struct -//! let holder = pin!(Holder::<'_, FooKey<'_>>::new_with(|foo| { +//! let holder = Box::pin(Holder::<'_, FooKey<'_>>::new_with(|foo| { //! foo.build(Foo { //! foo: Default::default(), //! t: stack_str, @@ -175,9 +169,9 @@ //! "PhantomDrop". //! //! When enabling both features, `nightly` takes over and we use the wrapper -//! always. This doesn't make a practical difference since the generated UB -//! check is dead code anyway, but `PhantomDrop` should be lighter on compile -//! times. +//! always. This doesn't make a significant difference since the generated UB +//! check is dead code anyway, but `PhantomDrop` doesn't depend on `alloc` and +//! can be used in `no_std` environments. //! //! If not using either feature, `T: ?Sized` support requires `unsafe`ly //! implementing `Opaque`. @@ -207,7 +201,7 @@ extern crate alloc; #[cfg(all(feature="alloc", not(feature="nightly")))] #[doc(hidden)] -pub use alloc::boxed::Box as UBCheck; +pub struct UBCheck<T: ?Sized>(alloc::boxed::Box<T>); #[cfg(feature="nightly")] #[doc(hidden)] @@ -572,10 +566,7 @@ impl<'k, T> Holder<'k, T> where T: Opaque { /// Simple example: /// /// ```rust - /// #![feature(pin_macro)] - /// /// use core::cell::Cell; - /// use core::pin::pin; /// /// use selfref::Holder; /// use selfref::opaque; @@ -593,7 +584,7 @@ impl<'k, T> Holder<'k, T> where T: Opaque { /// } /// /// fn main() { - /// let holder = pin!(Holder::<'_, FooKey>::new_with( + /// let holder = Box::pin(Holder::<'_, FooKey>::new_with( /// |foo| foo.build(Foo::default()) /// )); /// // Actually making our Foo refer to itself. @@ -608,12 +599,9 @@ impl<'k, T> Holder<'k, T> where T: Opaque { /// With lifetime parameters: /// /// ```rust - /// #![feature(pin_macro)] - /// /// use core::cell::Cell; /// use core::marker::PhantomData; /// use core::pin::Pin; - /// use core::pin::pin; /// /// use selfref::Holder; /// use selfref::opaque; @@ -634,7 +622,7 @@ impl<'k, T> Holder<'k, T> where T: Opaque { /// let stack_array: [u8; 5] = *b"hello"; /// // a non-'static &str /// let stack_str = core::str::from_utf8(&stack_array).unwrap(); - /// let holder = pin!(Holder::<'_, FooKey<'_>>::new_with(|foo| { + /// let holder = Box::pin(Holder::<'_, FooKey<'_>>::new_with(|foo| { /// foo.build(Foo { /// foo: Default::default(), /// t: stack_str, diff --git a/src/srce.rs b/src/srce.rs index e263ddc..8015522 100644 --- a/src/srce.rs +++ b/src/srce.rs @@ -15,11 +15,9 @@ //! Funny runtime specialization thing: //! //! ```rust -//! #![feature(pin_macro)] //! use core::cell::Cell; //! use core::marker::PhantomData; //! use core::pin::Pin; -//! use core::pin::pin; //! //! use qcell::LCell; //! @@ -62,7 +60,7 @@ //! //! fn main() { //! // Create the holder -//! let mut holder = pin!( +//! let mut holder = Box::pin( //! LCellEnvironment::<IntrusiveLCellKey<MyThing>>::new_with( //! |thing| thing.build(IntrusiveLCell { //! y: Cell::new(None), @@ -78,7 +76,7 @@ //! }); //! //! // Coerce to dyn Foo. -//! let mut holder: Pin<&mut LCellEnvironment<IntrusiveLCellKey<dyn Foo>>> = holder; +//! let mut holder: Pin<Box<LCellEnvironment<IntrusiveLCellKey<dyn Foo>>>> = holder; //! //! // Run some rw/mut operations on it. //! holder.as_mut().open_rw(|owner, thing| { @@ -181,10 +179,7 @@ impl<'k, T> LCellEnvironment<'k, T> where T: Opaque { /// # Examples /// /// ```rust - /// #![feature(pin_macro)] - /// /// use qcell::LCell; - /// use core::pin::pin; /// /// use selfref::srce::LCellEnvironment; /// use selfref::opaque; @@ -201,7 +196,7 @@ impl<'k, T> LCellEnvironment<'k, T> where T: Opaque { /// } /// /// fn main() { - /// let mut holder = pin!(LCellEnvironment::<'_, FooKey>::new_with( + /// let mut holder = Box::pin(LCellEnvironment::<'_, FooKey>::new_with( /// |foo| foo.build(Foo { foo: LCell::new(Default::default()) }) /// )); /// // Actually making our Foo refer to itself. @@ -239,10 +234,7 @@ impl<'k, T> LCellEnvironment<'k, T> where T: Opaque { /// # Examples /// /// ```rust - /// #![feature(pin_macro)] - /// /// use qcell::LCell; - /// use core::pin::pin; /// /// use selfref::srce::LCellEnvironment; /// use selfref::opaque; @@ -259,7 +251,7 @@ impl<'k, T> LCellEnvironment<'k, T> where T: Opaque { /// } /// /// fn main() { - /// let holder = pin!(LCellEnvironment::<'_, FooKey>::new_with( + /// let holder = Box::pin(LCellEnvironment::<'_, FooKey>::new_with( /// |foo| foo.build(Foo { foo: LCell::new(Default::default()) }) /// )); /// holder.as_ref().open_ro( diff --git a/tests/ui/no_uaf_2.rs b/tests/ui/no_uaf_2.rs index 49694b2..53e3ad8 100644 --- a/tests/ui/no_uaf_2.rs +++ b/tests/ui/no_uaf_2.rs @@ -1,9 +1,6 @@ // by steffahn -#![feature(pin_macro)] - use std::cell::Cell; -use std::pin::pin; use selfref::opaque; use selfref::Holder; @@ -25,7 +22,7 @@ fn main() { Holder::<'_, MyStructKey>::new_with(|builder| builder.build(MyStruct { cell: Default::default(), })); - let s = pin!(s); + let s = Box::pin(s); s.as_ref().operate_in(|r| { r.cell.set(&String::from("hello world")); // temporary dropped at end of this statement println!("{}", r.cell.get()) // accesses dropped `String` data diff --git a/tests/ui/no_uaf_2.stderr b/tests/ui/no_uaf_2.stderr index 5737a38..5b9fa55 100644 --- a/tests/ui/no_uaf_2.stderr +++ b/tests/ui/no_uaf_2.stderr @@ -1,10 +1,10 @@ error[E0716]: temporary value dropped while borrowed - --> tests/ui/no_uaf_2.rs:30:21 + --> tests/ui/no_uaf_2.rs:27:21 | -29 | s.as_ref().operate_in(|r| { +26 | s.as_ref().operate_in(|r| { | - has type `OperateIn<'1, MyStructKey>` -30 | r.cell.set(&String::from("hello world")); // temporary dropped at end of this statement +27 | r.cell.set(&String::from("hello world")); // temporary dropped at end of this statement | ------------^^^^^^^^^^^^^^^^^^^^^^^^^^^-- temporary value is freed at the end of this statement | | | - | | creates a temporary value which is freed while still in use + | | creates a temporary which is freed while still in use | argument requires that borrow lasts for `'1` |