# NOTE **This is an unpublished version (with bugs) forked from [enum_delegate](https://gitlab.com/dawn_app/enum_delegate.git) (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 ======= [gitlab](https://gitlab.com/dawn_app/enum_delegate) [crates.io](https://crates.io/crates/enum_delegate) [docs.rs](https://docs.rs/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. ```toml [dependencies] enum_delegate = "0.4" ``` ## Example ```rust 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 ```rust,ignore #[delegate] enum Name { First(FirstName), Last { name: LastName, }, } ``` #### Generated **Note**: Example is simplified for readability. ```rust,ignore impl Convert for Name { type Output = Either; 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 ```rust,ignore #[delegate] trait AsStr { fn as_str(&self) -> &str; } ``` #### Generated **Note**: Example is simplified for readability. ```rust,ignore // Implementation for "any variant of enum or struct" type. impl AsStr for Either { 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 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 like `Sized`, `Send` or `Sync` are not supported yet. - Associated types/constants are not supported yet. - `#[delegate(for(Enum))]` supports only concrete generic types. ## Alternatives ### [Dynamic dispatch][1] Rust mechanism for dynamic dispatch using trait objects, which adds runtime overhead. #### Example ```rust 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][2] `enum_delegate` was highly inspired by [enum_dispatch][2] 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][3] 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][1]. [1]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html [2]: https://docs.rs/enum_dispatch/latest/enum_dispatch [3]: https://docs.rs/enum_derive/latest/enum_derive