#lifetime #reference #ownership #borrowing #self-reference #self #self-referential

no-std allsorts-rental

一个宏,用于生成安全的自引用结构体,以及针对常用用例的预定义类型

1 个不稳定版本

使用旧的 Rust 2015

0.5.6 2021年2月5日

#10#self-reference


用于 allsorts_no_std

MIT/Apache

40KB
312

警告:此软件包已不再维护或支持

我不会移除这个软件包,因为它仍然适合其预期用途,但随着时间的推移,它将越来越不符合 Rust 的进化,因此鼓励用户探索其他解决方案。我也不会合并任何拉取请求,因为代码足够复杂,我不再自信能够有效地审查它们。Rental 在当前状态下可以被认为是“冻结”的,并且任何进一步的开发都需要在分叉下进行,供愿意这样做的人使用。

Rental - 一个宏,用于生成安全的自引用结构体,以及针对常用用例的预定义类型。

文档

概述

在设计 API 的过程中,有时会方便,甚至必要,允许结构体中的字段引用该结构体中的其他字段。Rust 的所有权和借用概念非常强大,但尚不能表达这种场景。

手动创建这样的结构体会需要不安全的代码来擦除字段类型的生命周期参数。直接访问字段将完全不安全。这个库通过允许在严格控制下通过封闭的闭包访问内部字段来解决这个问题,这些闭包由泛型生命周期限制以防止任何具有不正确生命周期的数据的渗透或泄漏。简而言之,虽然结构体内部使用不安全的代码来存储字段,但暴露给结构体消费者的接口是完全安全的。该接口的实现是微妙且冗长的,因此有一个宏来自动化这个过程。

此软件包的 API 由 rental 宏组成,该宏生成安全的自引用结构体,一些示例实例以展示此类结构体提供的 API(请参阅 examples),以及一个模块,用于预定义实例以覆盖常见用例(请参阅 common)。

示例

此crate的一个用途实例是当与libloading一起工作时。该crate提供了一个Library结构体,它定义了从其中借用Symbol的方法。这些符号由库的生命周期限制,因此被认为是借用。在正常情况下,无法在单个结构体中同时存储库和符号,但本crate中定义的宏允许您定义一个可以同时存储两者的结构体,如下所示

rental! {
    pub mod rent_libloading {
        use libloading;

        #[rental(deref_suffix)] // This struct will deref to the Deref::Target of Symbol.
        pub struct RentSymbol<S: 'static> {
            lib: Box<libloading::Library>, // Library is boxed for StableDeref.
            sym: libloading::Symbol<'lib, S>, // The 'lib lifetime borrows lib.
        }
    }
}

fn main() {
    let lib = libloading::Library::new("my_lib.so").unwrap(); // Open our dylib.
    if let Ok(rs) = rent_libloading::RentSymbol::try_new(
        Box::new(lib),
        |lib| unsafe { lib.get::<extern "C" fn()>(b"my_symbol") }) // Loading symbols is unsafe.
    {
        (*rs)(); // Call our function
    };
}

这样我们就可以在单个结构体中存储Library和借用它的Symbol。我们甚至可以告诉我们的结构体解引用到函数指针本身,这样我们就可以很容易地调用它。这是合法的,因为函数指针的类型签名中不包含由租赁结构体引入的任何特殊生命周期,这意味着重新借用不会将它们暴露给外界。顺便提一下,加载符号的unsafe块是必要的,因为从动态库中加载符号的操作是不安全的,且与租赁无关。

限制

由于rust本身的错误或待定功能,当前实现存在一些限制。一旦底层语言允许,这些限制将得到解除。

  • 目前,租赁结构体本身只能在某些条件下接受生命周期参数。这些条件难以完全描述,但一般来说,租赁结构体的生命周期参数必须出现在结构体字段类型签名中的任何特殊租赁生命周期“之外”。换句话说,用'static替换租赁生命周期仍然必须产生合法的类型,否则将无法编译。在大多数情况下,这是可以接受的,因为大多数此库的使用案例都涉及擦除所有生命周期,但没有理由为什么租赁结构体的头元素不能接受任意的生命周期参数。这目前无法完全支持,因为缺乏'unsafe生命周期或等效功能。
  • 前缀字段,以及如果是子租赁,则头字段必须具有以下形式Foo<T>,其中Foo是某些StableDeref容器,否则租赁将无法正确猜测类型的Deref::Target。如果您使用不满足此模式的自定义类型,则可以使用字段的target_ty属性手动指定目标类型。如果头字段不是子租赁,则它可以具有任何形式,只要它是StableDeref
  • 租赁结构体只能有最多32个租赁生命周期,包括来自子租赁的继承租赁生命周期。这种限制是由于需要为每个租赁 arity 实现一个新的特质。如果需要,这个限制可以很容易地增加。
  • 构造器闭包中接收到的引用目前没有在界限中表达它们之间的生命周期关系,因为HRTB 生命周期目前不支持界限。这并不是一个安全问题,但它阻止了一些其他有效的用法编译。

依赖

~1.5MB
~37K SLoC