#struct #fields #self-referential #macro #generate #closures #rental

macro rental-impl

Rental的实现细节。不应直接使用。

17个版本

使用旧的Rust 2015

0.5.5 2019年10月1日
0.5.4 2019年6月12日
0.5.2 2018年9月7日
0.5.0 2018年5月5日
0.0.1 2017年3月18日

#23 in #self-referential

Download history 1649/week @ 2024-03-14 2002/week @ 2024-03-21 2451/week @ 2024-03-28 1861/week @ 2024-04-04 1332/week @ 2024-04-11 1092/week @ 2024-04-18 1078/week @ 2024-04-25 957/week @ 2024-05-02 1168/week @ 2024-05-09 1035/week @ 2024-05-16 1028/week @ 2024-05-23 1262/week @ 2024-05-30 960/week @ 2024-06-06 922/week @ 2024-06-13 1007/week @ 2024-06-20 766/week @ 2024-06-27

3,799 每月下载量
2 crates 中使用

MIT/Apache

66KB
1.5K SLoC

警告:此crate不再维护或支持

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

Rental - 生成安全自引用结构的宏,以及用于常见用例的预制类型。

文档

概述

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

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

本软件包的API由一个宏组成,该宏名为rental,用于生成安全的自引用结构体。还有一些示例实例来展示这些结构体提供的API(参见examples),以及一个模块,其中包含预制的实例以覆盖常见用例(参见common)。

示例

此软件包在处理libloading时非常有用。该软件包提供了一个Library结构体,该结构体定义了从其中借用Symbol的方法。这些符号受库的生命周期限制,因此被视为借用。在正常情况下,人们无法在单个结构体中同时存储库和符号,但本软件包中定义的宏允许您定义能够同时存储这两个的结构的结构体,如下所示

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。我们甚至可以告诉我们的结构体解引用到函数指针本身,这样我们就可以很容易地调用它。这是合法的,因为函数指针的类型签名中不包含由rental结构体引入的特殊生命周期,这意味着重新借用不会将它们暴露给外界。顺便说一句,加载符号的unsafe块是必要的,因为从动态库中加载符号的操作是不安全的,这与rental无关。

限制

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

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

依赖

~1.5MB
~34K SLoC