2 个不稳定版本
0.2.0 | 2022年11月5日 |
---|---|
0.1.0 | 2022年11月2日 |
#14 在 #dynamic-dispatch
4,273 每月下载次数
用于 12 个包(直接使用3个)
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
无法也不能为它实现From
和TryInto
特征。
关联类型
enum_delegate
还可以处理具有关联类型的特征。
相同类型
默认情况下,enum_delegate
要求所有相关实现者提供相同关联类型的相同值。如果您指定不同的类型,则无法编译。
请查看 e2_associated_type.rs
示例,了解如何使用简单的关联类型。
混合类型
如果您需要在同一枚举中混合不同类型,则需要使用 #[enum_delegate(unify = "enum_wrap")]
注释您的枚举。这将生成一个新的枚举,用于包装关联类型的可能类型。会自动生成 From
和 TryInto
转换,以促进枚举与不同包装类型之间的转换。
这个“秘密”生成的枚举的名称不是公共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