Expand description
§Declarative generation of enum dispatch
Generate boilerplate code for dynamic dispatch of a trait using an enum. Also generates From for every enum variant
This is a fully declarative version of enum_dispatch macro
For benchmarks look at enum_dispatch benchmarks crate
Usage example:
use declarative_enum_dispatch::enum_dispatch;
enum_dispatch!(
pub trait ShapeTrait: Clone + std::fmt::Debug + 'static {
/// No return + default implementation
fn print_name(&self) {
println!("name: `{}`", self.name());
}
/// Basic call without arguments
fn name(&self) -> String;
fn area(&self) -> i32;
/// Mutable self + arguments
fn grow(&mut self, numerator: i32, denominator: i32,);
/// Kinda supports generics :) Bot not generic parameters, only `impl Trait`
fn greater(&self, other: &impl ShapeTrait) -> bool;
/// Supports async methods
async fn send(&self);
/// Works with attributes
#[cfg(feature = "platform_specific")]
fn platform_specific(self);
}
#[derive(Debug, Clone)]
pub enum Shape {
Rect(Rect),
Circle(Circle),
#[cfg(feature = "platform_specific")]
Cube(Cube)
}
);
#[derive(Debug, Clone)]
pub struct Rect{ w: i32, h: i32 }
#[derive(Debug, Clone)]
pub struct Circle { r: i32 }
impl ShapeTrait for Rect {
fn print_name(&self) {
println!("rect name: `{}`", self.name());
}
fn name(&self) -> String {
"Rect".to_string()
}
fn area(&self) -> i32 {
self.w * self.h
}
fn grow(&mut self, numerator: i32, denominator: i32) {
self.w = self.w * numerator / denominator;
self.h = self.h * numerator / denominator;
}
fn greater(&self, other: &impl ShapeTrait) -> bool {
self.area() > other.area()
}
async fn send(&self) {}
}
impl ShapeTrait for Circle {
fn name(&self) -> String {
"Circle".to_string()
}
fn area(&self) -> i32 {
// close enough PI approximation :)
3 * self.r * self.r
}
fn grow(&mut self, numerator: i32, denominator: i32 ) {
self.r = self.r * numerator / denominator;
}
fn greater(&self, other: &impl ShapeTrait) -> bool {
self.area() > other.area()
}
async fn send(&self) {}
}
assert_eq!(Shape::Rect(Rect { w: 1, h: 1 }).name(), "Rect".to_string());
assert_eq!(Shape::Circle(Circle { r: 1 }).name(), "Circle".to_string());
§Macro expansion
Expansion of the macro above
use declarative_enum_dispatch::enum_dispatch;
pub trait ShapeTrait: Clone + std::fmt::Debug + 'static {
/// No return + default implementation
fn print_name(&self) {
println!("name: `{}`", self.name());
}
/// Basic call without arguments
fn name(&self) -> String;
fn area(&self) -> i32;
/// Mutable self + arguments
fn grow(&mut self, numerator: i32, denominator: i32);
/// Kinda supports generics :) Bot not generic parameters, only `impl Trait`
fn greater(&self, other: &impl ShapeTrait) -> bool;
/// Supports async methods
async fn send(&self);
/// Works with attributes
#[cfg(feature = "platform_specific")]
fn platform_specific(self);
}
#[derive(Debug, Clone)]
pub enum Shape {
Rect(Rect),
Circle(Circle),
#[cfg(feature = "platform_specific")]
Cube(Cube),
}
impl ShapeTrait for Shape {
/// No return + default implementation
fn print_name(&self) {
match self {
Shape::Rect(v) => v.print_name(),
Shape::Circle(v) => v.print_name(),
#[cfg(feature = "platform_specific")]
Shape::Cube(v) => v.print_name(),
}
}
/// Basic call without arguments
fn name(&self) -> String {
match self {
Shape::Rect(v) => v.name(),
Shape::Circle(v) => v.name(),
#[cfg(feature = "platform_specific")]
Shape::Cube(v) => v.name(),
}
}
fn area(&self) -> i32 {
match self {
Shape::Rect(v) => v.area(),
Shape::Circle(v) => v.area(),
#[cfg(feature = "platform_specific")]
Shape::Cube(v) => v.area(),
}
}
/// Mutable self + arguments
fn grow(&mut self, numerator: i32, denominator: i32) {
match self {
Shape::Rect(v) => v.grow(numerator, denominator),
Shape::Circle(v) => v.grow(numerator, denominator),
#[cfg(feature = "platform_specific")]
Shape::Cube(v) => v.grow(numerator, denominator),
}
}
/// Kinda supports generics :) Bot not generic parameters, only `impl Trait`
fn greater(&self, other: &impl ShapeTrait) -> bool {
match self {
Shape::Rect(v) => v.greater(other),
Shape::Circle(v) => v.greater(other),
#[cfg(feature = "platform_specific")]
Shape::Cube(v) => v.greater(other),
}
}
/// Supports async methods
async fn send(&self) {
match self {
Shape::Rect(v) => v.send().await,
Shape::Circle(v) => v.send().await,
#[cfg(feature = "platform_specific")]
Shape::Cube(v) => v.send().await,
}
}
/// Works with attributes
#[cfg(feature = "platform_specific")]
fn platform_specific(self) {
match self {
Shape::Rect(v) => v.platform_specific(),
Shape::Circle(v) => v.platform_specific(),
#[cfg(feature = "platform_specific")]
Shape::Cube(v) => v.platform_specific(),
}
}
}
impl From<Rect> for Shape {
fn from(value: Rect) -> Shape {
Shape::Rect(value)
}
}
impl From<Circle> for Shape {
fn from(value: Circle) -> Shape {
Shape::Circle(value)
}
}
#[cfg(feature = "platform_specific")]
impl From<Cube> for Shape {
fn from(value: Cube) -> Shape {
Shape::Cube(value)
}
}