selfref 0.2.1

Pain-free self-referential pinned types
Documentation

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:

#![feature(generic_associated_types)]

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:

#![feature(generic_associated_types)]

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

# #![feature(generic_associated_types)]
#
# use std::cell::Cell;
#
use selfref::Holder;
use selfref::new_with_closure;
# use selfref::opaque;
use selfref::operate_in_closure;
#
# struct MySelfRefStruct<'this> {
#     this: Cell<Option<&'this MySelfRefStruct<'this>>>,
# }
#
# struct MySelfRefStructKey;
#
# opaque! {
#     impl Opaque for MySelfRefStructKey {
#         type Kind<'this> = MySelfRefStruct<'this>;
#     }
# }

fn main() {
// first, construct the struct
let holder = Box::pin(Holder::<'_, MySelfRefStructKey>::new_with(
new_with_closure::<MySelfRefStructKey, _>(|_| {
MySelfRefStruct {
this: Cell::new(None)
}
})
));

// then, build the self-reference
holder.as_ref().operate_in(
operate_in_closure::<MySelfRefStructKey, _, _>(|this| {
this.this.set(Some(this.get_ref()));
})
);
}

Nightly

This crate (currently) requires nightly, as it relies on GATs.

Examples

This is a more complex example borrowing from an external lifetime:

#![feature(generic_associated_types)]
#![feature(pin_macro)]

use core::cell::Cell;
use core::marker::PhantomData;
use core::pin::Pin;
use core::pin::pin;

use selfref::Holder;
use selfref::NewWith;
use selfref::OperateIn;
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() {
// can't use real closure, "hand-roll" a closure.
struct FooBuilder<'b>(&'b str);
impl<'k, 'b: 'k> NewWith<'k, FooKey<'b>> for FooBuilder<'b> {
fn new_with<'a>(self) -> Foo<'a, 'b> where 'k: 'a {
Foo {
foo: Default::default(),
t: self.0,
}
}
}

// 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::new_with(FooBuilder(stack_str)));

// setup the self-reference
struct SetFooRef;
impl<'k, 'b: 'k> OperateIn<'k, FooKey<'b>> for SetFooRef {
type Out = ();
fn operate_in<'a>(self, foo: Pin<&'a Foo<'a, 'b>>)
where 'k: 'a {
foo.foo.set(Some(foo.get_ref()));
}
}
holder.as_ref().operate_in(SetFooRef);
}