#static #downcast #type-id #any #macro-derive #invariants

transient

支持非静态类型的 std::any::Any 的重实现

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 模式

Download history 301/week @ 2024-06-09 407/week @ 2024-06-16 180/week @ 2024-06-23 245/week @ 2024-06-30 97/week @ 2024-07-07 60/week @ 2024-07-14 63/week @ 2024-07-21

每月494次下载

MIT/Apache

115KB
930

transient

此crate提供了一个重实现的 std::any::Any trait,支持具有非静态生命周期的类型。

Crates.io

文档

模块文档和示例

用法

要将此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的实现来通过仅通过InvAny特质进行参数化来利用与'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类型(默认情况下)被分配一个TransienceInv<'a>,这限制了它们被擦除到(并从)dyn Any<Inv<'a>>特质对象。相比之下,'static类型实现最灵活的Transience(),这允许它们将自身转换为任何他们想要的dyn Any<_>,包括默认的dyn Any()

在这两种极端之间有一个很大的中间地带,这在文档中有详细的讨论(提示:还有CoContra类型),但关键是类型在实现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);
}

许可证

本项目采用以下任一许可证授权:

供您选择。

依赖

~0–7MB
~27K SLoC