Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

proxy-enum

CrateAPI

Emulate dynamic dispatch and "sealed classes" using a proxy enum, which defers all method calls to its variants.

Introduction

In rust, dynamic dispatch is done using trait objects (dyn Trait). They enable us to have runtime polymorphism, a way of expressing that a type implements a certain trait while ignoring its concrete implementation.

let animal:&dynAnimal = random_animal();
animal.feed();// may print "mew", "growl" or "squeak"

Trait objects come with a downside though: getting a concrete implementation back from a trait object (downcasting) is painfull. (see [std::any::Any])

If you know there are only a finite number of implentations to work with, an enum might be better at expressing such a relationship:

enumAnimal{Cat(Cat),Lion(Lion),Mouse(Mouse)}matchrandom_animal(){Animal::Cat(cat) => cat.feed(),Animal::Lion(lion) => lion.feed(),Animal::Mouse(mouse) => mouse.feed()}

Some languages have special support for such types, like Kotlin with so called "sealed classes".

Rust, however, does not.

proxy-enum simplifies working with such types using procedural macros.

Usage

#[proxy_enum::proxy(Animal)]mod proxy {enumAnimal{Cat(Cat),Lion(Lion),Mouse(Mouse)}implAnimal{#[implement]fnfeed(&self){}}}

This will expand to:

mod proxy {enumAnimal{Cat(Cat),Lion(Lion),Mouse(Mouse)}implAnimal{fnfeed(&self){matchself{Animal::Cat(cat) => cat.feed(),Animal::Lion(lion) => lion.feed(),Animal::Mouse(mouse) => mouse.feed()}}}implFrom<Cat>forAnimal{/* ... */}implFrom<Lion>forAnimal{/* ... */}implFrom<Mouse>forAnimal{/* ... */}}

This, however, will only compile if Cat, Lion and Mouse all have a method called feed. Since rust has traits to express common functionality, trait implentations can be generated too:

#[proxy_enum::proxy(Animal)]mod proxy {enumAnimal{Cat(Cat),Lion(Lion),Mouse(Mouse)}traitEat{fnfeed(&self);}#[implement]implEatforAnimal{}}

Since the macro has to know which methods the trait contains, it has to be defined within the module. However, implementations for external traits can be generated too:

#[proxy_enum::proxy(Animal)]mod proxy {enumAnimal{Cat(Cat),Lion(Lion),Mouse(Mouse)}#[external(std::string::ToString)]traitToString{fnto_string(&self) -> String;}#[implement]impl std::string::ToStringforAnimal{}}

About

Emulate dynamic dispatch and sealed classes using a proxy enum, which defers all method calls to its variants.

Resources

Stars

19 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages