29 个版本
使用旧的 Rust 2015
0.5.6 | 2021 年 5 月 1 日 |
---|---|
0.5.5 | 2019 年 10 月 1 日 |
0.5.4 | 2019 年 6 月 12 日 |
0.5.3 | 2019 年 3 月 14 日 |
0.1.0 |
|
#8 in #自引用
5,259 每月下载
在不到 19 个 crates 中使用
40KB
312 行
警告:此包不再维护或支持
我不会从仓库中移除此包,因为它仍然适合其预期用途,但随着时间的推移,它将越来越不符合 Rust 的演进,因此鼓励用户探索其他解决方案。我也不会合并任何 pull 请求,因为代码已经足够复杂,我不再自信能够有效地审查它们。Rental 可以视为当前状态的“冻结”,任何进一步的开发都需要在 fork 下进行,由任何希望进行此操作的人进行。
租赁 - 一个宏,用于生成安全的自引用结构体,以及为常用用例预先定义的类型。
概述
在设计 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个租赁生存期,包括子租赁的传递性租赁生存期。这种限制是由于需要为每个租赁算术实现一个新的特性。如果需要,这个限制可以很容易地增加。
- 构造函数闭包中接收到的引用目前没有在边界中表达它们之间的生存期关系,因为HRTB生存期目前不支持边界。这不是一个安全性漏洞,但它阻止了一些其他有效使用情况的编译。
依赖关系
~1.5MB
~35K SLoC