1 个不稳定版本

0.1.0 2024年7月30日

696Rust 模式 中排名

Download history 121/week @ 2024-07-28 2/week @ 2024-08-04

每月123次下载

MIT 许可证

21KB
144 代码行

x-selfref

这基本上是对 MIT 许可证的 selfref 库(由另一位作者)的一个复制/分支,我增加了一些功能。

selfref 介绍

Rust 中自引用结构的实验性方法。

这个crate提供了一个替代的自引用结构方法,而不是提供一个宏或框架,你可以在其中定义一个自引用结构,然后由它为你处理所有细节,我们尝试暴露抽象和构建块,以便在安全 Rust 中使自引用结构运行良好。

例如,一个 Holder 是一个围绕自引用结构的包装器,提供安全的 API 来构建和操作自引用结构。但是,与其它自引用crate不同,它不指定结构的底层存储。使用 Opaque 特性来识别用于与 Holder 一起使用的自引用结构 - 由于 Rust 不支持高阶类型(HKTs),这个crate使用泛型关联类型(GATs)作为解决方案。

要使用这个crate,首先使用纯 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>>>,
}

然后,定义一个类型来实现 Opaque。这可以通过使用 opaque 宏自动完成

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

现在你可以构建一个 Holder 并为它选择一个存储。例如,在 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()));
        }
    );
}

示例

这是一个更复杂的示例,它借用自外部生命周期

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()));
    });
}

功能

由于 PhantomData 不可靠,我们目前需要以下功能来支持在 xselfref::opaque! 中的 T: ?Sized

  • alloc - 为 T: ?Sized 提供 xselfref::opaque!,由 Box 实现。
  • nightly - 为 T: ?Sized 提供 xselfref::opaque!,通过在 PhantomData 周围添加一个包装器来解决上述问题。我们称其为 "PhantomDrop"。

当同时启用这两个功能时,nightly 将接管并始终使用包装器。由于生成的 UB 检查已经是死代码,因此这不会带来显著差异,但 PhantomDrop 不依赖于 alloc,并且可以在 no_std 环境中使用。

如果不使用任何功能,则需要通过 unsafe 方式实现 Opaque 以支持 T: ?Sized

请注意,我们默认不会启用任何功能!我们假设大多数人不是来使用这个 crate 的 T: ?Sized 支持的,因此这些是 crate 依赖的最佳默认设置。如果他们确实需要 ?Sized 支持的话,他们只需启用其中之一(可能是 alloc)。

无运行时依赖