# 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
=======
[](https://gitlab.com/dawn_app/enum_delegate)
[](https://crates.io/crates/enum_delegate)
[](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