Expand description
Pain-free self-referential/recursively-referential pinned types.
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).
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:
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>;
}
}
That’s because there’s no way to create the reference
&'this MySelfRefStruct<'this>
before constructing the struct! That is,
you cannot do this:
struct MySelfRefStruct<'this> {
this: &'this MySelfRefStruct<'this>,
}
fn main() {
let x = MySelfRefStruct { this: &x };
}
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:
use std::cell::Cell;
use selfref::opaque;
struct MySelfRefStruct<'this> {
this: Cell<Option<&'this MySelfRefStruct<'this>>>,
}
struct MySelfRefStructKey;
opaque! {
impl Opaque for MySelfRefStructKey {
type Kind<'this> = MySelfRefStruct<'this>;
}
}
and then we can use it:
// lines from the above example have been omitted
use selfref::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:
#![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;
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 = 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
selfref::opaque!
:
alloc
-selfref::opaque!
forT: ?Sized
is provided byBox
.nightly
-selfref::opaque!
forT: ?Sized
is 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 practical difference since the generated UB
check is dead code anyway, but PhantomDrop
should be lighter on compile
times.
If not using either feature, T: ?Sized
support requires unsafe
ly
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
Structs
T::Kind
.Pin<&'k T::Kind<'k>>
for implied bounds.