Expand description
§x-selfref
This basically a copy/fork of the MIT-licensed selfref library (by another author), with a few additions made by me.
§Introduction of selfref
An experimental approach to self-referential structs in Rust.
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.
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.
To use the crate, first define a self-referential struct in plain Rust:
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>>>,
}Then, define a type to implement the Opaque. This can be done
automatically with the opaque macro:
use xselfref::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.Now you can construct a Holder and pick a storage for it. For example,
in a Box:
use xselfref::Holder;
fn main() {
// first, construct the struct
let holder = Box::pin(Holder::<'_, MySelfRefStructKey>::new_with(
|foo| foo.build({
MySelfRefStruct {
this: Cell::new(None)
}
})
));
// then, build the self-reference
holder.as_ref().operate_in(
|this| {
this.this.set(Some(this.get_ref()));
}
);
}§Examples
This is a more complex example borrowing from an external lifetime:
use core::cell::Cell;
use core::marker::PhantomData;
use core::pin::Pin;
use xselfref::Holder;
use xselfref::opaque;
struct Foo<'a, 'b: 'a> {
foo: Cell<Option<&'a Foo<'a, 'b>>>,
t: &'b str,
}
struct FooKey<'b>(PhantomData<&'b str>);
opaque! {
impl['b] Opaque for FooKey<'b> {
type Kind<'a> = Foo<'a, 'b>;
}
}
fn main() {
// a non-'static &str
let stack_array: [u8; 5] = *b"hello";
let stack_str = core::str::from_utf8(&stack_array).unwrap();
// construct the struct
let holder = Box::pin(Holder::<'_, FooKey<'_>>::new_with(|foo| {
foo.build(Foo {
foo: Default::default(),
t: stack_str,
})
}));
holder.as_ref().operate_in(|foo| {
foo.foo.set(Some(foo.get_ref()));
});
}§Features
Due to PhantomData is unsound
we currently require the following features for T: ?Sized support in
xselfref::opaque!:
alloc-xselfref::opaque!forT: ?Sizedis provided byBox.nightly-xselfref::opaque!forT: ?Sizedis provided by a wrapper aroundPhantomData, which works around the above issue. we call this “PhantomDrop”.
When enabling both features, nightly takes over and we use the wrapper
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 unsafely
implementing Opaque.
Note that we do not enable any features by default! We assume most
folks aren’t coming to this crate for its T: ?Sized support, so these are
the best defaults for crates to depend on. If they do need the ?Sized
support they can just enable one of these (probably alloc).
Macros§
- opaque
- Creates an opaqueified self-referential struct “key”.
Structs§
- Builder
- Helper for creating a
Holder. - Holder
- Holds an “opaqueified”
T::Kind. - Operate
In - Wrapper around a
Pin<&'k T::Kind<'k>>for implied bounds.
Traits§
- NonSelfref
Part - Opaque
- An opaqueified self-referential struct “key”.