diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lib.rs | 3 | ||||
-rw-r--r-- | src/srce.rs | 293 |
2 files changed, 296 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs index fdefe7d..17c4c10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -223,6 +223,9 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for UBCheck<T> { fn drop(&mut self) {} } +#[cfg(feature="qcell")] +pub mod srce; + /// An opaqueified self-referential struct "key". /// /// # Safety diff --git a/src/srce.rs b/src/srce.rs new file mode 100644 index 0000000..e263ddc --- /dev/null +++ b/src/srce.rs @@ -0,0 +1,293 @@ +// SRCE - Self-referential cell environments +// Copyright (C) 2022-2023 Soni L. +// This software is made with love by a queer trans person. +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! SRCE is a way to open and close an `LCellOwner`. This provides the full +//! expressive power of `selfref`, with the ability to use zeroish-cost +//! `LCell`s in the struct. +//! +//! This module requires crate feature `qcell`. +//! +//! # Examples +//! +//! 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; +//! +//! use selfref::srce::LCellEnvironment; +//! +//! // Some traits we wanna have +//! trait Foo { +//! fn update(&mut self); +//! } +//! trait Bar { +//! fn get(&self) -> String; +//! } +//! +//! // Thing that impls multiple of them +//! struct MyThing; +//! impl Foo for MyThing { +//! fn update(&mut self) { +//! println!("hello world!"); +//! } +//! } +//! impl Bar for MyThing { +//! fn get(&self) -> String { +//! return String::from("hello"); +//! } +//! } +//! +//! // The container for the above. +//! // T is gonna become dyn Foo later +//! struct IntrusiveLCell<'a, T: ?Sized> { +//! y: Cell<Option<&'a LCell<'a, dyn Bar>>>, +//! x: LCell<'a, T>, +//! } +//! // The "key" or "kind" struct for `selfref` to do the magic. +//! struct IntrusiveLCellKey<T: ?Sized>(PhantomData<T>); +//! selfref::opaque! { +//! impl[T: ?Sized] Opaque for IntrusiveLCellKey<T> { +//! type Kind<'this> = IntrusiveLCell<'this, T>; +//! } +//! } +//! +//! fn main() { +//! // Create the holder +//! let mut holder = pin!( +//! LCellEnvironment::<IntrusiveLCellKey<MyThing>>::new_with( +//! |thing| thing.build(IntrusiveLCell { +//! y: Cell::new(None), +//! x: LCell::new(MyThing), +//! }) +//! ) +//! ); +//! +//! // Create the `&LCell<dyn Bar>`. doesn't actually require rw because of +//! // the `Cell`! +//! holder.as_ref().open_ro(|_, thing| { +//! thing.y.set(Some(&thing.get_ref().x)); +//! }); +//! +//! // Coerce to dyn Foo. +//! let mut holder: Pin<&mut LCellEnvironment<IntrusiveLCellKey<dyn Foo>>> = holder; +//! +//! // Run some rw/mut operations on it. +//! holder.as_mut().open_rw(|owner, thing| { +//! owner.rw(&thing.x).update(); +//! }); +//! +//! // Run some ro/ref operations on it +//! holder.as_ref().open_ro(|owner, thing| { +//! if let Some(y) = thing.y.get() { +//! owner.ro(y).get(); +//! } +//! }); +//! } +//! ``` + +use qcell::LCellOwner; +use crate::{Builder, Holder, Opaque, OperateIn}; + +use core::pin::Pin; + +/// A self-referential environment of `LCell`s, with zero runtime cost. +/// +/// Wrapper around [`Holder`]. Like `Holder`, the lifetime may be anything. +/// +/// # Examples +/// +/// ```rust +/// use qcell::LCell; +/// +/// use selfref::srce::LCellEnvironment; +/// use selfref::opaque; +/// +/// struct Foo<'a> { +/// foo: LCell<'a, Option<&'a Foo<'a>>>, +/// } +/// +/// struct FooKey; +/// opaque! { +/// impl Opaque for FooKey { +/// type Kind<'a> = Foo<'a>; +/// } +/// } +/// +/// fn main() { +/// // We can use a closure here, but we need to give the compiler a hint. +/// let holder = LCellEnvironment::<'_, FooKey>::new_with( +/// |foo| foo.build(Foo { foo: LCell::new(Default::default()) }) +/// ); +/// } +/// ``` +pub struct LCellEnvironment<'k, T: Opaque + 'k> { + inner: Holder<'k, T>, +} + +impl<'k, T> LCellEnvironment<'k, T> where T: Opaque { + /// Creates a new `LCellEnvironment`. + /// + /// Works exactly like [`Holder::new_with`]. + /// + /// # Examples + /// + /// ```rust + /// use qcell::LCell; + /// + /// use selfref::srce::LCellEnvironment; + /// use selfref::opaque; + /// + /// struct Foo<'a> { + /// foo: LCell<'a, Option<&'a Foo<'a>>>, + /// } + /// + /// struct FooKey; + /// opaque! { + /// impl Opaque for FooKey { + /// type Kind<'a> = Foo<'a>; + /// } + /// } + /// + /// fn main() { + /// // We can use a closure here, but we need to help the compiler. + /// let holder = LCellEnvironment::<'_, FooKey>::new_with( + /// |foo| foo.build(Foo { foo: LCell::new(Default::default()) }) + /// ); + /// } + /// ``` + pub fn new_with<F>(f: F) -> Self + where + F: for<'a> FnOnce(&mut Builder<'a, T>), + T::Kind<'k>: Sized, + { + Self { + inner: Holder::new_with(f) + } + } +} + +impl<'k, T> LCellEnvironment<'k, T> where T: Opaque { + /// Opens this LCellEnvironment for reading and writing (mutable). + /// + /// # Examples + /// + /// ```rust + /// #![feature(pin_macro)] + /// + /// use qcell::LCell; + /// use core::pin::pin; + /// + /// use selfref::srce::LCellEnvironment; + /// use selfref::opaque; + /// + /// struct Foo<'a> { + /// foo: LCell<'a, Option<&'a Foo<'a>>>, + /// } + /// + /// struct FooKey; + /// opaque! { + /// impl Opaque for FooKey { + /// type Kind<'a> = Foo<'a>; + /// } + /// } + /// + /// fn main() { + /// let mut holder = pin!(LCellEnvironment::<'_, FooKey>::new_with( + /// |foo| foo.build(Foo { foo: LCell::new(Default::default()) }) + /// )); + /// // Actually making our Foo refer to itself. + /// holder.as_mut().open_rw( + /// |owner, foo| { + /// *owner.rw(&foo.foo) = Some(foo.get_ref()); + /// } + /// ); + /// } + /// ``` + pub fn open_rw<'i, F, R>(self: Pin<&'i mut Self>, f: F) -> R + where + F: for<'a, 'id> FnOnce(&'a mut LCellOwner<'id>, OperateIn<'id, T>) -> R, + { + unsafe { + self.map_unchecked_mut(|this| &mut this.inner) + }.into_ref().operate_in(|env| { + // SAFETY? we're still unsure if this is entirely sound! + // the LCellOwner is owned by us, and the borrow given to the + // closure is mutable if we're mutable, and shared if we're shared. + // while this is mixing concerns ('id is being used for 2 different + // things), it is also invariant as far as the owner is concerned, + // and (for rw access), it is also unique. + // thus, we tentatively consider this sound. + // see https://users.rust-lang.org/t/soundness-review-request-srce-0-1/87587 + let guard = unsafe { + generativity::Guard::new(generativity::Id::new()) + }; + f(&mut LCellOwner::new(guard), env) + }) + } + + /// Opens this LCellEnvironment for reading only (shared). + /// + /// # Examples + /// + /// ```rust + /// #![feature(pin_macro)] + /// + /// use qcell::LCell; + /// use core::pin::pin; + /// + /// use selfref::srce::LCellEnvironment; + /// use selfref::opaque; + /// + /// struct Foo<'a> { + /// foo: LCell<'a, Option<&'a Foo<'a>>>, + /// } + /// + /// struct FooKey; + /// opaque! { + /// impl Opaque for FooKey { + /// type Kind<'a> = Foo<'a>; + /// } + /// } + /// + /// fn main() { + /// let holder = pin!(LCellEnvironment::<'_, FooKey>::new_with( + /// |foo| foo.build(Foo { foo: LCell::new(Default::default()) }) + /// )); + /// holder.as_ref().open_ro( + /// |owner, foo| { + /// // can't write to it + /// assert!(owner.ro(&foo.foo).is_none()); + /// } + /// ); + /// } + /// ``` + pub fn open_ro<'i, F, R>(self: Pin<&'i Self>, f: F) -> R + where + F: for<'a, 'id> FnOnce(&'a LCellOwner<'id>, OperateIn<'id, T>) -> R, + { + unsafe { + self.map_unchecked(|this| &this.inner) + }.operate_in(|env| { + // SAFETY? we're still unsure if this is entirely sound! + // the LCellOwner is owned by us, and the borrow given to the + // closure is mutable if we're mutable, and shared if we're shared. + // while this is mixing concerns ('id is being used for 2 different + // things), it is also invariant as far as the owner is concerned. + // thus, we tentatively consider this sound. + // see https://users.rust-lang.org/t/soundness-review-request-srce-0-1/87587 + let guard = unsafe { + generativity::Guard::new(generativity::Id::new()) + }; + f(&LCellOwner::new(guard), env) + }) + } +} |