#enums #dynamic-dispatch #traits #optimization #performance #macro

enum_delegate

轻松用枚举替换动态调度,以获得速度和序列化

2 个不稳定版本

0.2.0 2022年11月5日
0.1.0 2022年11月2日

#14#dynamic-dispatch

Download history 839/week @ 2024-03-13 859/week @ 2024-03-20 845/week @ 2024-03-27 1101/week @ 2024-04-03 866/week @ 2024-04-10 950/week @ 2024-04-17 836/week @ 2024-04-24 996/week @ 2024-05-01 1165/week @ 2024-05-08 929/week @ 2024-05-15 1297/week @ 2024-05-22 1243/week @ 2024-05-29 1239/week @ 2024-06-05 1025/week @ 2024-06-12 919/week @ 2024-06-19 898/week @ 2024-06-26

4,273 每月下载次数
用于 12 个包(直接使用3个)

MIT/Apache

55KB
1K SLoC

轻松用枚举替换动态调度,以获得速度和序列化。

功能说明

在 Rust 中,您可以使用类似 Box<dyn YourTrait> 的方式来持有您的 trait 的任何可能实现。但这有点慢,并且与序列化不兼容。解决方案是将所有需要的实现放在枚举中。这个库可以自动为枚举实现 trait 以及一系列有用的转换。然后,您可以使用枚举而不是动态调度。

tldr:类似于惊人的 enum_dispatch,但更加出色。

概述

enum_delegate 添加到您计划使用它的每个包

enum_delegate = "0.1"

使用 #[enum_delegate::register] 注释您的 trait

#[enum_delegate::register]
trait SayHello {
    fn say_hello(&self, name: &str) -> String;
}

创建一个枚举,其变体形式类似于 VariantName(VariantType)。每个变体字段都必须实现您的 trait。现在,使用 #[enum_delegate::implement(trait name)] 注释枚举。

struct Arthur;
impl SayHello for Arthur {...}

struct Pablo;
impl SayHello for Pablo {...}

#[enum_delegate::implement(SayHello)]
enum People {
    Arthur(Arthur),
    Pablo(Pablo),
}

您的 trait 将为枚举实现,并生成如 From<Arthur>TryInto<Arthur> 之类的转换。您现在可以像使用 Box<dyn SayHello> 一样使用它,但速度会更快!

您可以在 e1_simple.rs 示例中看到它的实际效果。

第三方 Traits

如果特征来自第三方crate,并且无法向其添加 enum_delegate::register,怎么办?没关系!只需将特征定义作为第二个参数传递给 enum_delegate::implement

#[enum_delegate::implement(SayHello,
    trait SayHello {
        fn say_hello(&self, name: &str) -> String;
    }
)]
enum People {
    Arthur(Arthur),
    Pablo(Pablo),
}

与之前一样,你的特征将为枚举实现。还会生成像 From<Arthur>TryInto<Arthur> 这样的转换。

第三方枚举

如果枚举来自第三方crate,并且无法向其添加 enum_delegate::implement,怎么办?这同样没问题!使用 enum_delegate::implement_for 属性,并将特征名称及特征定义传递给它

#[enum_delegate::implement_for(People,
    enum People {
        Arthur(Arthur),
        Pablo(Pablo),
    }
)]
trait SayHello {
    fn say_hello(&self, name: &str) -> String;
}

这将实现指定枚举的特征。

注意:由于枚举来自另一个crate,在这种情况下,enum_delegate 无法也不能为它实现 FromTryInto 特征。

关联类型

enum_delegate 还可以处理具有关联类型的特征。

相同类型

默认情况下,enum_delegate 要求所有相关实现者提供相同关联类型的相同值。如果您指定不同的类型,则无法编译。

请查看 e2_associated_type.rs 示例,了解如何使用简单的关联类型。

混合类型

如果您需要在同一枚举中混合不同类型,则需要使用 #[enum_delegate(unify = "enum_wrap")] 注释您的枚举。这将生成一个新的枚举,用于包装关联类型的可能类型。会自动生成 FromTryInto 转换,以促进枚举与不同包装类型之间的转换。

这个“秘密”生成的枚举的名称不是公共API的稳定部分。使用 YourEnum::YourAssociatedType 来引用此类型,并使用 From 转换来构造它。

请查看 e3_mixed_associated_type.rs 示例,了解混合关联类型。

限制

一些边缘情况,例如泛型特征或超特征,目前尚不支持。

替代方案

  • enum_dispatch,该crate从中得到灵感,解决了相同的问题。然而,如以下讨论,enum_delegate 有一些额外的功能。
  • 动态分派:您不必将类型包装在枚举中,例如,可以使用 Box<dyn YourTrait>。然而,这将较慢(见 benchmarks)且默认情况下无法序列化。不过,typetag 可以帮助序列化。
  • enum_derive:使用 enum_derive::EnumInnerAsTrait 继承一个方法,返回内部值的借用指针,并转换为特质对象。尽管这样做速度较慢,但我并不知道这样做相比 enum_delegate 有什么优势。
  • 手动委托特性和其他枚举技巧:虽然麻烦,但在某些情况下可能提供更多灵活性。
enum_dispatch 的比较

🟡 性能:相同。这是预期的,因为它们生成的代码非常相似。(参见存储库中的 benchmarks。)

✅ 支持跨存储库。由于 enum_dispatch 的实现存在技术限制,它只能在特性和枚举在同一个存储库中使用时使用。enum_delegate 则允许您将它们放在不同的存储库中。(参见存储库中的 cross_crate_example。)

✅ 误差更好。由于技术限制,在某些情况下,enum_dispatch 可能会无声地失败。使用 enum_delegate,您的代码要么成功,要么无法编译。诚然,某些错误消息并不完美,但至少您会知道出了问题。(参见存储库中的 tests_error。)

✅ 关联类型。 enum_delegate 对关联类型有一些支持,但 enum_dispatch 没有。(参见存储库中的 examples。)

依赖项

~2MB
~40K SLoC