Enums in Rust are very pleasant to work with because they allow opt-out comprehensive case handling. They allow to express polychotomies which naturally occur in many situations. For me, they are perfect reification of sets in the mathematical sense; arguably, the second most important concept in mathematics after (proper) classes. Their instances, in turn, represent choices of elements from the sets.
However, as reification of sets they lack some useful features [=sugar].
-
adhoc trait impementations
// Will work if every type in all enum items implements MakeSound
adhoc impl MakeSound for Animal;
- orders on enums as sets
enum Author {
CharlesDickens,
GeorgeOrwell,
WilliamShakespeare,
GeorgeEliot,
//...
}
order Alphabetic;
// Implementation of the order can come from #[derive(Alphabetic)] or std can offer
// Lexicographic order instead
order Alphabetic on Author {
CharlesDickens,
GeorgeDickens,
GeorgeEliot,
WilliamShakespeare,
//..
}
// Iter GAT should be defined if every type in all enum items implements Default
let authors_iter = Alphabetic::Iter<Author>::new();
- enum sum (union in the mathematical sense)
enum IOError {
// ...
}
enum ReqHandlingError {
// ...
}
// Any error in this case is either IOError or ReqHandlingError.
// Name collisions are expected to be caught by the compiler
enum Error = IOError | FormatError;
fn handle_req() -> Result<(),Error> {
let req = recv_req().map_err(Into<Error>::into)?;
req.handle().map_err(Into<Error>::into)?;
}
Update: July 11, 2022
The enum Error = {IOError::*, FormatError::*};
syntax seems to be more flexible due to interaction with as
keyword.
-
For cases where only some syntactically identical expressions with enum items are of the same type,
adhoc
can be used to avoid boilerplate as well. -
Multiple layers of inclusion should be traversed with a
Ker::<...Kernel>::ker()
function.
// 2 in 1 example
use crate::{Result, MainChef};
// derive defines MenuItemKer enum and implements Ker::<MenuItemKer>
#[derive(Ker)]
enum MenuItem = {
MainCourse::*,
Salad::*,
Drink::*,
};
#[accountable_personnel("Pierre Gagnaire <...@gmail.com>")]
enum MainCourse {
ButterChicken,
#[dish_of_the_day]
BalakPaneer,
RoganJosh,
}
#[accountable_personnel("Marco Pierre White <...@gmail.com>")]
enum Salad {
Green,
Michigan,
Ceasar,
Ambrosia,
}
#[derive(Ker)]
enum Drink {
Wine::*,
Pop::*,
Juice::*,
}
#[accountable_personnel("Josh Peck <...@gmail.com>")]
enum Wine {
Barbera,
Cabernet Franc,
Cabernet Sauvignon,
Carignan,
}
// No one is accountable for pop
enum Pop {
Pepsi,
Coke,
}
fn handle_complaint(c : &Complaint) -> Result<()> {
// Complaint { id, menu_item, text, time }
match c.menu_item.ker() {
MenuItem::MainCourse(mc) => {
if matches!(mc, MainCourse::DISH_OF_THE_DAY) { MainChef::notify_about_complaint(c) }?;
MainCourse::notify_about_complaint(c),
}
MenuItem::Salad(_) => Salad::notify_about_complaint(c),
MenuItem::Drink(d) => {
match d.ker() => {
Drink::Pop => Ok(()),
// drink: D should be another form of irrefutable pattern
_drink: D => adhoc D::notify_about_complaint(c),
}
}
}
}
Any ideas?