1 unstable release
0.3.0 | Feb 17, 2023 |
---|
#1367 in Rust patterns
16KB
176 lines
NOTE
This is an unpublished version (with bugs) forked from enum_delegate (revision f99967517e925f9630735836fb1601747df9c71d
).
The original authors are Reinis Mazeiks, 50U10FCA7. This crate is to resolve dependency conflicts in another crate concurrent_tor
. Since that crate depends on enum_delegate ^= "0.3.0"
,
this is a temporary upload of enum_delegate
.
enum_delegate
Provides trait delegation functionality for enums and structs.
Note: Name contains enum_*
part due to historical reasons. It is not limited to enums.
[dependencies]
enum_delegate = "0.4"
Example
use enum_delegate::delegate;
#[delegate(for(LastName))]
trait AsStr {
fn as_str(&self) -> &str;
}
impl AsStr for String {
fn as_str(&self) -> &str {
self
}
}
#[delegate(derive(AsStr))]
struct FirstName(String);
#[delegate]
struct LastName {
name: String,
}
#[delegate(derive(AsStr))]
enum Name {
First(FirstName),
Last(LastName),
}
fn main() {
let name = Name::First(FirstName("John".to_string()));
assert_eq!(name.as_str(), "John");
let name = Name::Last(LastName {
name: "Doe".to_string(),
});
assert_eq!(name.as_str(), "Doe");
}
How it works
Crate provides several definitions:
delegate
macro - derives trait on a new-type struct or enum, invoking it on its inner type.Convert
trait - converts enum or struct to type represents "any of its variant".
#[delegate]
expansion on type
Implements Convert
trait to enum/struct, which allows to convert it to "any of its variant" type.
Source
#[delegate]
enum Name {
First(FirstName),
Last {
name: LastName,
},
}
Generated
Note: Example is simplified for readability.
impl Convert for Name {
type Output = Either<FirstName, LastName>;
fn convert(self) -> Self::Output {
match self {
Name::First(first_name) => Either::Left(first_name),
Name::Last { name } => Either::Right(name),
}
}
}
#[delegate]
expansion on trait
Implements the trait for any type that implements Convert
trait, which "any variant" implements target trait.
I.e. each method in generated impl
convert self
to "any of its variant" and invokes target trait method on it.
Source
#[delegate]
trait AsStr {
fn as_str(&self) -> &str;
}
Generated
Note: Example is simplified for readability.
// Implementation for "any variant of enum or struct" type.
impl<L: AsStr, R: AsStr> AsStr for Either<L, R> {
fn as_str(&self) -> &str {
match self {
Either::Left(left) => left.as_str(),
Either::Right(right) => right.as_str(),
}
}
}
// Implementation for any type that implements `Convert` trait.
impl<T> AsStr for T
where
T: Convert,
T::Output: AsStr,
{
fn as_str(&self) -> &str {
let this = self.convert(); // convert type to "any of its variant".
this.as_str() // call the method.
}
}
Limitations
- Both struct/enum and trait should be marked with
#[delegate]
macro attribute. - Struct or enum variant should contain only single field.
- Trait methods must have an untyped receiver.
- Implementation for external (remote) types or traits is not supported yet.
- Supertraits or
Self
trait/method bounds except marker traits likeSized
,Send
orSync
are not supported yet. - Associated types/constants are not supported yet.
#[delegate(for(Enum<Generics>))]
supports only concrete generic types.
Alternatives
Dynamic dispatch
Rust mechanism for dynamic dispatch using trait objects, which adds runtime overhead.
Example
trait AsStr {
fn as_str(&self) -> &str;
}
impl AsStr for String {
fn as_str(&self) -> &str {
self
}
}
struct FirstName(String);
impl AsStr for FirstName {
fn as_str(&self) -> &str {
&self.0
}
}
fn do_something_with_string(s: &dyn AsStr) {
println!("{}", s.as_str());
}
fn main() {
let name = "John".to_string();
do_something_with_string(&name);
let name = FirstName(name);
do_something_with_string(&name);
}
enum_dispatch
enum_delegate
was highly inspired by enum_dispatch crate. It provides similar functionality, but has more limitations:
- Supports only enums.
- Using
enum_dispatch
between crates is impossible due to limitations of its design. - Order-dependent macro expansion (in some cases your code fails if items marked with the macro has different order than macro expects).
enum_derive
Derive a method to return a borrowed pointer to the inner value, cast to a trait object, using enum_derive::EnumInnerAsTrait
.
Slower though, more similar to Dynamic dispatch.
Dependencies
~4MB
~85K SLoC