xselfref 0.1.0

Fork of the selfref library, with some additions
Documentation
  • Coverage
  • 71.43%
    10 out of 14 items documented6 out of 12 items with examples
  • Size
  • Source code size: 19.94 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 1.68 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Links
  • Homepage
  • FeanorTheElf/xselfref
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • FeanorTheElf

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 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 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 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 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.

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! for T: ?Sized is provided by Box.
  • nightly - xselfref::opaque! for T: ?Sized is provided by a wrapper around PhantomData, 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).