7 个不稳定版本 (3 个破坏性更新)
0.4.0 | 2024年6月30日 |
---|---|
0.3.0 | 2024年6月23日 |
0.2.1 | 2024年6月19日 |
0.1.2 | 2024年6月15日 |
#465 in Rust 模式
每月494次下载
115KB
930 行
transient
此crate提供了一个重实现的 std::any::Any
trait,支持具有非静态生命周期的类型。
文档
用法
要将此crate引入您的仓库,请将 transient
添加到您的 Cargo.toml
中,或运行 cargo add transient
。
使用此crate的第一步是为一个类型实现提供的 Transient
trait,这可以通过包含的 derive
宏或手动定义两个关联类型来实现。手动实现此trait是 unsafe
的,但简单直接且文档齐全,一旦实现,就可以安全地使用此crate的功能。
以下示例演示了为静态类型推导 Transient
trait 的简单情况,然后将它转换为 dyn Any
trait 对象,以模拟动态类型,就像使用stdlib的实现在做一样。
use transient::{Transient, Any, Downcast};
#[derive(Transient, Debug, PartialEq)]
struct MyUsize(usize);
fn main() {
let orig = MyUsize(5);
// we can erase the 'static type...
let erased: &dyn Any = &orig;
assert!(erased.is::<MyUsize>());
// and restore it...
let restored: &MyUsize = erased.downcast_ref().unwrap();
assert_eq!(restored, &orig);
// and use it in dynamically-typed shenanigans...
let bananas = "bananas".to_string();
let stuff = vec![erased, &orig.0, restored, &bananas];
assert_eq!(stuff[0].downcast_ref::<MyUsize>().unwrap(), &orig);
}
有趣的地方在于,当存在一个包含借用数据的非-'static
类型时,由于它的'static
边界,它不能与std::any::Any
实现一起使用。以下示例将演示如何使用transient
crate的实现来通过仅通过Inv
对Any
特质进行参数化来利用与'static
类型相同的功能,其中Inv
是一个Transience
实现,它绑定了stdlib无法处理的生存期和变异性信息。
use transient::{Transient, Any, Inv, Downcast};
#[derive(Transient, Debug, PartialEq)]
struct MyUsizeRef<'a>(&'a usize);
fn main() {
let five = 5;
let orig = MyUsizeRef(&five);
// we can erase the non-'static type...
let erased: &dyn Any<Inv> = &orig;
assert!(erased.is::<MyUsizeRef>());
// and restore it...
let restored: &MyUsizeRef = erased.downcast_ref().unwrap();
assert_eq!(restored, &orig);
// and use it in dynamically-typed shenanigans...
let stuff = vec![erased, &five, restored, &"bananas"];
assert_eq!(stuff[0].downcast_ref::<MyUsizeRef>().unwrap(), &orig);
}
上面使用的Inv
类型代表“不变”,这是已知属性“变异性”最保守的形式,它描述了类型相对于生存期参数的行为。对变异性有理解将让您能够利用此crate的高级功能,但对于大多数用途来说不是必需的,因为上述显示的Inv
类型可以安全地用于具有单个生存期参数的任何类型。
在第一个示例中,我们没有指定Transience
类型,将我们的类型转换为裸的dyn Any
,此时隐式选择了Any
特质的默认类型参数()
,这使得它像stdlib的实现一样,只接受'static
类型;尝试使用第二个示例中定义的MyUsizeRef
将会被编译器拒绝。这暗示了实现此crate所使用的底层机制,即在实现Transient
特质时,类型声明了它们的时间关系(即Transience
),然后它将dyn Any<_>
变体允许它们使用的范围进行边界限制。使用derive宏实现Transient
的非'static
类型(默认情况下)被分配一个Transience
为Inv<'a>
,这限制了它们被擦除到(并从)dyn Any<Inv<'a>>
特质对象。相比之下,'static
类型实现最灵活的Transience
为()
,这允许它们将自身转换为任何他们想要的dyn Any<_>
,包括默认的dyn Any()
。
在这两种极端之间有一个很大的中间地带,这在文档中有详细的讨论(提示:还有Co
和Contra
类型),但关键是类型在实现Transient
特质时对其时间行为做出单一但直接的决策,然后使用类型系统和特质边界将其他所有内容保持为安全。
用法:多个生存期参数
上述展示的机制可以自然地扩展到具有多个生命周期参数的类型,通过将 dyn Any<_>
参数化为一个元组,如下例所示;然而,当前包含的 derive
宏仅支持零个或一个生命周期参数的类型,因此我们这次将自行实现 Transient
特性
use transient::{Transient, Any, Inv, Downcast};
#[derive(Debug, PartialEq)]
struct TwoRefs<'a, 'b>(&'a i32, &'b i32);
unsafe impl<'a, 'b> Transient for TwoRefs<'a, 'b> {
// the `Static` type is simply the same as the `Self` type with its
// lifetimes replaced by `'static`
type Static = TwoRefs<'static, 'static>;
// we use a tuple for the `Transience` that covers both lifetimes, using
// `Inv` for each element since this is always safe
type Transience = (Inv<'a>, Inv<'b>);
}
fn main() {
let (five, seven) = (5, 7);
let orig = TwoRefs(&five, &seven);
let erased: &dyn Any<Inv> = &orig;
assert!(erased.is::<TwoRefs>());
let restored: &TwoRefs = erased.downcast_ref().unwrap();
assert_eq!(restored, &orig);
let stuff = vec![erased, &five, restored, &"bananas"];
assert_eq!(stuff[0].downcast_ref::<TwoRefs>().unwrap(), &orig);
}
许可证
本项目采用以下任一许可证授权:
- Apache License, Version 2.0, (LICENSE-APACHE 或 https://www.apache.org/licenses/LICENSE-2.0)
- MIT 许可证 (LICENSE-MIT 或 https://opensource.org/licenses/MIT)
供您选择。
依赖
~0–7MB
~27K SLoC