Skip to content

Commit 85c3803

Browse files
committed
add README.md
1 parent 43b5606 commit 85c3803

File tree

1 file changed

+115
-0
lines changed

1 file changed

+115
-0
lines changed

README.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# proxy-enum
2+
3+
Emulate dynamic dispatch and ["sealed classes"](https://kotlinlang.org/docs/reference/sealed-classes.html) using a proxy enum, which defers all method calls to its variants.
4+
5+
## Introduction
6+
In rust, dynamic dispatch is done using trait objects (`dyn Trait`).
7+
They enable us to have runtime polymorphism, a way of expressing that a type implements a
8+
certain trait while ignoring its concrete implementation.
9+
10+
```
11+
let animal: &dyn Animal = random_animal();
12+
animal.feed(); // may print "mew", "growl" or "squeak"
13+
```
14+
15+
Trait objects come with a downside though:
16+
getting a concrete implementation back from a trait object (downcasting) is painfull.
17+
(see [std::any::Any])
18+
19+
If you know there are only a finite number of implentations to work with, an `enum` might be
20+
better at expressing such a relationship:
21+
```
22+
enum Animal {
23+
Cat(Cat),
24+
Lion(Lion),
25+
Mouse(Mouse)
26+
}
27+
28+
match random_animal() {
29+
Animal::Cat(cat) => cat.feed(),
30+
Animal::Lion(lion) => lion.feed(),
31+
Animal::Mouse(mouse) => mouse.feed()
32+
}
33+
```
34+
Some languages have special support for such types, like Kotlin with so called "sealed classes".
35+
36+
Rust, however, does *not*.
37+
38+
`proxy-enum` simplifies working with such types using procedural macros.
39+
40+
## Usage
41+
```
42+
#[proxy_enum::proxy(Animal)]
43+
mod proxy {
44+
enum Animal {
45+
Cat(Cat),
46+
Lion(Lion),
47+
Mouse(Mouse)
48+
}
49+
50+
impl Animal {
51+
#[implement]
52+
fn feed(&self) {}
53+
}
54+
}
55+
```
56+
This will expand to:
57+
```
58+
mod proxy {
59+
enum Animal {
60+
Cat(Cat),
61+
Lion(Lion),
62+
Mouse(Mouse)
63+
}
64+
65+
impl Animal {
66+
fn feed(&self) {
67+
match self {
68+
Animal::Cat(cat) => cat.feed(),
69+
Animal::Lion(lion) => lion.feed(),
70+
Animal::Mouse(mouse) => mouse.feed()
71+
}
72+
}
73+
}
74+
}
75+
```
76+
This, however, will only compile if `Cat`, `Lion` and `Mouse` all have a method called `feed`.
77+
Since rust has traits to express common functionality, trait implentations can be generated too:
78+
```
79+
#[proxy_enum::proxy(Animal)]
80+
mod proxy {
81+
enum Animal {
82+
Cat(Cat),
83+
Lion(Lion),
84+
Mouse(Mouse)
85+
}
86+
87+
trait Eat {
88+
fn feed(&self);
89+
}
90+
91+
#[implement]
92+
impl Eat for Animal {}
93+
}
94+
```
95+
Since the macro has to know which methods the trait contains, it has to be defined within the
96+
module. However, implementations for external traits can be generated too:
97+
98+
```
99+
#[proxy_enum::proxy(Animal)]
100+
mod proxy {
101+
enum Animal {
102+
Cat(Cat),
103+
Lion(Lion),
104+
Mouse(Mouse)
105+
}
106+
107+
#[external(std::string::ToString)]
108+
trait ToString {
109+
fn to_string(&self) -> String;
110+
}
111+
112+
#[implement]
113+
impl std::string::ToString for Animal {}
114+
}
115+
```

0 commit comments

Comments
 (0)